PageRenderTime 1091ms CodeModel.GetById 191ms app.highlight 672ms RepoModel.GetById 131ms app.codeStats 0ms

/src/gtkscintilla2/scintilla/lexers/LexOthers.cxx

https://github.com/anoopjohn/gphpedit
C++ | 1290 lines | 1120 code | 32 blank | 138 comment | 276 complexity | 8d17fbc4205db8c76a96be69507e3a62 MD5 | raw file
   1// Scintilla source code edit control
   2/** @file LexOthers.cxx
   3 ** Lexers for batch files, diff results, properties files, make files and error lists.
   4 ** Also lexer for LaTeX documents.
   5 **/
   6// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
   7// The License.txt file describes the conditions under which this software may be distributed.
   8
   9#include <stdlib.h>
  10#include <string.h>
  11#include <stdio.h>
  12#include <stdarg.h>
  13#include <assert.h>
  14#include <ctype.h>
  15
  16#include "ILexer.h"
  17#include "Scintilla.h"
  18#include "SciLexer.h"
  19
  20#include "PropSetSimple.h"
  21#include "WordList.h"
  22#include "LexAccessor.h"
  23#include "Accessor.h"
  24#include "StyleContext.h"
  25#include "CharacterSet.h"
  26#include "LexerModule.h"
  27
  28#ifdef SCI_NAMESPACE
  29using namespace Scintilla;
  30#endif
  31
  32static bool strstart(const char *haystack, const char *needle) {
  33	return strncmp(haystack, needle, strlen(needle)) == 0;
  34}
  35
  36static bool Is0To9(char ch) {
  37	return (ch >= '0') && (ch <= '9');
  38}
  39
  40static bool Is1To9(char ch) {
  41	return (ch >= '1') && (ch <= '9');
  42}
  43
  44static bool IsAlphabetic(int ch) {
  45	return isascii(ch) && isalpha(ch);
  46}
  47
  48static inline bool AtEOL(Accessor &styler, unsigned int i) {
  49	return (styler[i] == '\n') ||
  50	       ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
  51}
  52
  53// Tests for BATCH Operators
  54static bool IsBOperator(char ch) {
  55	return (ch == '=') || (ch == '+') || (ch == '>') || (ch == '<') ||
  56		(ch == '|') || (ch == '?') || (ch == '*');
  57}
  58
  59// Tests for BATCH Separators
  60static bool IsBSeparator(char ch) {
  61	return (ch == '\\') || (ch == '.') || (ch == ';') ||
  62		(ch == '\"') || (ch == '\'') || (ch == '/') || (ch == ')');
  63}
  64
  65static void ColouriseBatchLine(
  66    char *lineBuffer,
  67    unsigned int lengthLine,
  68    unsigned int startLine,
  69    unsigned int endPos,
  70    WordList *keywordlists[],
  71    Accessor &styler) {
  72
  73	unsigned int offset = 0;	// Line Buffer Offset
  74	unsigned int cmdLoc;		// External Command / Program Location
  75	char wordBuffer[81];		// Word Buffer - large to catch long paths
  76	unsigned int wbl;		// Word Buffer Length
  77	unsigned int wbo;		// Word Buffer Offset - also Special Keyword Buffer Length
  78	WordList &keywords = *keywordlists[0];      // Internal Commands
  79	WordList &keywords2 = *keywordlists[1];     // External Commands (optional)
  80
  81	// CHOICE, ECHO, GOTO, PROMPT and SET have Default Text that may contain Regular Keywords
  82	//   Toggling Regular Keyword Checking off improves readability
  83	// Other Regular Keywords and External Commands / Programs might also benefit from toggling
  84	//   Need a more robust algorithm to properly toggle Regular Keyword Checking
  85	bool continueProcessing = true;	// Used to toggle Regular Keyword Checking
  86	// Special Keywords are those that allow certain characters without whitespace after the command
  87	// Examples are: cd. cd\ md. rd. dir| dir> echo: echo. path=
  88	// Special Keyword Buffer used to determine if the first n characters is a Keyword
  89	char sKeywordBuffer[10];	// Special Keyword Buffer
  90	bool sKeywordFound;		// Exit Special Keyword for-loop if found
  91
  92	// Skip initial spaces
  93	while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
  94		offset++;
  95	}
  96	// Colorize Default Text
  97	styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
  98	// Set External Command / Program Location
  99	cmdLoc = offset;
 100
 101	// Check for Fake Label (Comment) or Real Label - return if found
 102	if (lineBuffer[offset] == ':') {
 103		if (lineBuffer[offset + 1] == ':') {
 104			// Colorize Fake Label (Comment) - :: is similar to REM, see http://content.techweb.com/winmag/columns/explorer/2000/21.htm
 105			styler.ColourTo(endPos, SCE_BAT_COMMENT);
 106		} else {
 107			// Colorize Real Label
 108			styler.ColourTo(endPos, SCE_BAT_LABEL);
 109		}
 110		return;
 111	// Check for Drive Change (Drive Change is internal command) - return if found
 112	} else if ((IsAlphabetic(lineBuffer[offset])) &&
 113		(lineBuffer[offset + 1] == ':') &&
 114		((isspacechar(lineBuffer[offset + 2])) ||
 115		(((lineBuffer[offset + 2] == '\\')) &&
 116		(isspacechar(lineBuffer[offset + 3]))))) {
 117		// Colorize Regular Keyword
 118		styler.ColourTo(endPos, SCE_BAT_WORD);
 119		return;
 120	}
 121
 122	// Check for Hide Command (@ECHO OFF/ON)
 123	if (lineBuffer[offset] == '@') {
 124		styler.ColourTo(startLine + offset, SCE_BAT_HIDE);
 125		offset++;
 126	}
 127	// Skip next spaces
 128	while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
 129		offset++;
 130	}
 131
 132	// Read remainder of line word-at-a-time or remainder-of-word-at-a-time
 133	while (offset < lengthLine) {
 134		if (offset > startLine) {
 135			// Colorize Default Text
 136			styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
 137		}
 138		// Copy word from Line Buffer into Word Buffer
 139		wbl = 0;
 140		for (; offset < lengthLine && wbl < 80 &&
 141		        !isspacechar(lineBuffer[offset]); wbl++, offset++) {
 142			wordBuffer[wbl] = static_cast<char>(tolower(lineBuffer[offset]));
 143		}
 144		wordBuffer[wbl] = '\0';
 145		wbo = 0;
 146
 147		// Check for Comment - return if found
 148		if (CompareCaseInsensitive(wordBuffer, "rem") == 0) {
 149			styler.ColourTo(endPos, SCE_BAT_COMMENT);
 150			return;
 151		}
 152		// Check for Separator
 153		if (IsBSeparator(wordBuffer[0])) {
 154			// Check for External Command / Program
 155			if ((cmdLoc == offset - wbl) &&
 156				((wordBuffer[0] == ':') ||
 157				(wordBuffer[0] == '\\') ||
 158				(wordBuffer[0] == '.'))) {
 159				// Reset Offset to re-process remainder of word
 160				offset -= (wbl - 1);
 161				// Colorize External Command / Program
 162				if (!keywords2) {
 163					styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
 164				} else if (keywords2.InList(wordBuffer)) {
 165					styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
 166				} else {
 167					styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
 168				}
 169				// Reset External Command / Program Location
 170				cmdLoc = offset;
 171			} else {
 172				// Reset Offset to re-process remainder of word
 173				offset -= (wbl - 1);
 174				// Colorize Default Text
 175				styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
 176			}
 177		// Check for Regular Keyword in list
 178		} else if ((keywords.InList(wordBuffer)) &&
 179			(continueProcessing)) {
 180			// ECHO, GOTO, PROMPT and SET require no further Regular Keyword Checking
 181			if ((CompareCaseInsensitive(wordBuffer, "echo") == 0) ||
 182				(CompareCaseInsensitive(wordBuffer, "goto") == 0) ||
 183				(CompareCaseInsensitive(wordBuffer, "prompt") == 0) ||
 184				(CompareCaseInsensitive(wordBuffer, "set") == 0)) {
 185				continueProcessing = false;
 186			}
 187			// Identify External Command / Program Location for ERRORLEVEL, and EXIST
 188			if ((CompareCaseInsensitive(wordBuffer, "errorlevel") == 0) ||
 189				(CompareCaseInsensitive(wordBuffer, "exist") == 0)) {
 190				// Reset External Command / Program Location
 191				cmdLoc = offset;
 192				// Skip next spaces
 193				while ((cmdLoc < lengthLine) &&
 194					(isspacechar(lineBuffer[cmdLoc]))) {
 195					cmdLoc++;
 196				}
 197				// Skip comparison
 198				while ((cmdLoc < lengthLine) &&
 199					(!isspacechar(lineBuffer[cmdLoc]))) {
 200					cmdLoc++;
 201				}
 202				// Skip next spaces
 203				while ((cmdLoc < lengthLine) &&
 204					(isspacechar(lineBuffer[cmdLoc]))) {
 205					cmdLoc++;
 206				}
 207			// Identify External Command / Program Location for CALL, DO, LOADHIGH and LH
 208			} else if ((CompareCaseInsensitive(wordBuffer, "call") == 0) ||
 209				(CompareCaseInsensitive(wordBuffer, "do") == 0) ||
 210				(CompareCaseInsensitive(wordBuffer, "loadhigh") == 0) ||
 211				(CompareCaseInsensitive(wordBuffer, "lh") == 0)) {
 212				// Reset External Command / Program Location
 213				cmdLoc = offset;
 214				// Skip next spaces
 215				while ((cmdLoc < lengthLine) &&
 216					(isspacechar(lineBuffer[cmdLoc]))) {
 217					cmdLoc++;
 218				}
 219			}
 220			// Colorize Regular keyword
 221			styler.ColourTo(startLine + offset - 1, SCE_BAT_WORD);
 222			// No need to Reset Offset
 223		// Check for Special Keyword in list, External Command / Program, or Default Text
 224		} else if ((wordBuffer[0] != '%') &&
 225				   (wordBuffer[0] != '!') &&
 226			(!IsBOperator(wordBuffer[0])) &&
 227			(continueProcessing)) {
 228			// Check for Special Keyword
 229			//     Affected Commands are in Length range 2-6
 230			//     Good that ERRORLEVEL, EXIST, CALL, DO, LOADHIGH, and LH are unaffected
 231			sKeywordFound = false;
 232			for (unsigned int keywordLength = 2; keywordLength < wbl && keywordLength < 7 && !sKeywordFound; keywordLength++) {
 233				wbo = 0;
 234				// Copy Keyword Length from Word Buffer into Special Keyword Buffer
 235				for (; wbo < keywordLength; wbo++) {
 236					sKeywordBuffer[wbo] = static_cast<char>(wordBuffer[wbo]);
 237				}
 238				sKeywordBuffer[wbo] = '\0';
 239				// Check for Special Keyword in list
 240				if ((keywords.InList(sKeywordBuffer)) &&
 241					((IsBOperator(wordBuffer[wbo])) ||
 242					(IsBSeparator(wordBuffer[wbo])))) {
 243					sKeywordFound = true;
 244					// ECHO requires no further Regular Keyword Checking
 245					if (CompareCaseInsensitive(sKeywordBuffer, "echo") == 0) {
 246						continueProcessing = false;
 247					}
 248					// Colorize Special Keyword as Regular Keyword
 249					styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_WORD);
 250					// Reset Offset to re-process remainder of word
 251					offset -= (wbl - wbo);
 252				}
 253			}
 254			// Check for External Command / Program or Default Text
 255			if (!sKeywordFound) {
 256				wbo = 0;
 257				// Check for External Command / Program
 258				if (cmdLoc == offset - wbl) {
 259					// Read up to %, Operator or Separator
 260					while ((wbo < wbl) &&
 261						(wordBuffer[wbo] != '%') &&
 262						(wordBuffer[wbo] != '!') &&
 263						(!IsBOperator(wordBuffer[wbo])) &&
 264						(!IsBSeparator(wordBuffer[wbo]))) {
 265						wbo++;
 266					}
 267					// Reset External Command / Program Location
 268					cmdLoc = offset - (wbl - wbo);
 269					// Reset Offset to re-process remainder of word
 270					offset -= (wbl - wbo);
 271					// CHOICE requires no further Regular Keyword Checking
 272					if (CompareCaseInsensitive(wordBuffer, "choice") == 0) {
 273						continueProcessing = false;
 274					}
 275					// Check for START (and its switches) - What follows is External Command \ Program
 276					if (CompareCaseInsensitive(wordBuffer, "start") == 0) {
 277						// Reset External Command / Program Location
 278						cmdLoc = offset;
 279						// Skip next spaces
 280						while ((cmdLoc < lengthLine) &&
 281							(isspacechar(lineBuffer[cmdLoc]))) {
 282							cmdLoc++;
 283						}
 284						// Reset External Command / Program Location if command switch detected
 285						if (lineBuffer[cmdLoc] == '/') {
 286							// Skip command switch
 287							while ((cmdLoc < lengthLine) &&
 288								(!isspacechar(lineBuffer[cmdLoc]))) {
 289								cmdLoc++;
 290							}
 291							// Skip next spaces
 292							while ((cmdLoc < lengthLine) &&
 293								(isspacechar(lineBuffer[cmdLoc]))) {
 294								cmdLoc++;
 295							}
 296						}
 297					}
 298					// Colorize External Command / Program
 299					if (!keywords2) {
 300						styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
 301					} else if (keywords2.InList(wordBuffer)) {
 302						styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
 303					} else {
 304						styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
 305					}
 306					// No need to Reset Offset
 307				// Check for Default Text
 308				} else {
 309					// Read up to %, Operator or Separator
 310					while ((wbo < wbl) &&
 311						(wordBuffer[wbo] != '%') &&
 312						(wordBuffer[wbo] != '!') &&
 313						(!IsBOperator(wordBuffer[wbo])) &&
 314						(!IsBSeparator(wordBuffer[wbo]))) {
 315						wbo++;
 316					}
 317					// Colorize Default Text
 318					styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT);
 319					// Reset Offset to re-process remainder of word
 320					offset -= (wbl - wbo);
 321				}
 322			}
 323		// Check for Argument  (%n), Environment Variable (%x...%) or Local Variable (%%a)
 324		} else if (wordBuffer[0] == '%') {
 325			// Colorize Default Text
 326			styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT);
 327			wbo++;
 328			// Search to end of word for second % (can be a long path)
 329			while ((wbo < wbl) &&
 330				(wordBuffer[wbo] != '%') &&
 331				(!IsBOperator(wordBuffer[wbo])) &&
 332				(!IsBSeparator(wordBuffer[wbo]))) {
 333				wbo++;
 334			}
 335			// Check for Argument (%n) or (%*)
 336			if (((Is0To9(wordBuffer[1])) || (wordBuffer[1] == '*')) &&
 337				(wordBuffer[wbo] != '%')) {
 338				// Check for External Command / Program
 339				if (cmdLoc == offset - wbl) {
 340					cmdLoc = offset - (wbl - 2);
 341				}
 342				// Colorize Argument
 343				styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_IDENTIFIER);
 344				// Reset Offset to re-process remainder of word
 345				offset -= (wbl - 2);
 346			// Check for Expanded Argument (%~...) / Variable (%%~...)
 347			} else if (((wbl > 1) && (wordBuffer[1] == '~')) ||
 348				((wbl > 2) && (wordBuffer[1] == '%') && (wordBuffer[2] == '~'))) {
 349				// Check for External Command / Program
 350				if (cmdLoc == offset - wbl) {
 351					cmdLoc = offset - (wbl - wbo);
 352				}
 353				// Colorize Expanded Argument / Variable
 354				styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
 355				// Reset Offset to re-process remainder of word
 356				offset -= (wbl - wbo);
 357			// Check for Environment Variable (%x...%)
 358			} else if ((wordBuffer[1] != '%') &&
 359				(wordBuffer[wbo] == '%')) {
 360				wbo++;
 361				// Check for External Command / Program
 362				if (cmdLoc == offset - wbl) {
 363					cmdLoc = offset - (wbl - wbo);
 364				}
 365				// Colorize Environment Variable
 366				styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
 367				// Reset Offset to re-process remainder of word
 368				offset -= (wbl - wbo);
 369			// Check for Local Variable (%%a)
 370			} else if (
 371				(wbl > 2) &&
 372				(wordBuffer[1] == '%') &&
 373				(wordBuffer[2] != '%') &&
 374				(!IsBOperator(wordBuffer[2])) &&
 375				(!IsBSeparator(wordBuffer[2]))) {
 376				// Check for External Command / Program
 377				if (cmdLoc == offset - wbl) {
 378					cmdLoc = offset - (wbl - 3);
 379				}
 380				// Colorize Local Variable
 381				styler.ColourTo(startLine + offset - 1 - (wbl - 3), SCE_BAT_IDENTIFIER);
 382				// Reset Offset to re-process remainder of word
 383				offset -= (wbl - 3);
 384			}
 385		// Check for Environment Variable (!x...!)
 386		} else if (wordBuffer[0] == '!') {
 387			// Colorize Default Text
 388			styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT);
 389			wbo++;
 390			// Search to end of word for second ! (can be a long path)
 391			while ((wbo < wbl) &&
 392				(wordBuffer[wbo] != '!') &&
 393				(!IsBOperator(wordBuffer[wbo])) &&
 394				(!IsBSeparator(wordBuffer[wbo]))) {
 395				wbo++;
 396			}
 397			if (wordBuffer[wbo] == '!') {
 398				wbo++;
 399				// Check for External Command / Program
 400				if (cmdLoc == offset - wbl) {
 401					cmdLoc = offset - (wbl - wbo);
 402				}
 403				// Colorize Environment Variable
 404				styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
 405				// Reset Offset to re-process remainder of word
 406				offset -= (wbl - wbo);
 407			}
 408		// Check for Operator
 409		} else if (IsBOperator(wordBuffer[0])) {
 410			// Colorize Default Text
 411			styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT);
 412			// Check for Comparison Operator
 413			if ((wordBuffer[0] == '=') && (wordBuffer[1] == '=')) {
 414				// Identify External Command / Program Location for IF
 415				cmdLoc = offset;
 416				// Skip next spaces
 417				while ((cmdLoc < lengthLine) &&
 418					(isspacechar(lineBuffer[cmdLoc]))) {
 419					cmdLoc++;
 420				}
 421				// Colorize Comparison Operator
 422				styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_OPERATOR);
 423				// Reset Offset to re-process remainder of word
 424				offset -= (wbl - 2);
 425			// Check for Pipe Operator
 426			} else if (wordBuffer[0] == '|') {
 427				// Reset External Command / Program Location
 428				cmdLoc = offset - wbl + 1;
 429				// Skip next spaces
 430				while ((cmdLoc < lengthLine) &&
 431					(isspacechar(lineBuffer[cmdLoc]))) {
 432					cmdLoc++;
 433				}
 434				// Colorize Pipe Operator
 435				styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_OPERATOR);
 436				// Reset Offset to re-process remainder of word
 437				offset -= (wbl - 1);
 438			// Check for Other Operator
 439			} else {
 440				// Check for > Operator
 441				if (wordBuffer[0] == '>') {
 442					// Turn Keyword and External Command / Program checking back on
 443					continueProcessing = true;
 444				}
 445				// Colorize Other Operator
 446				styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_OPERATOR);
 447				// Reset Offset to re-process remainder of word
 448				offset -= (wbl - 1);
 449			}
 450		// Check for Default Text
 451		} else {
 452			// Read up to %, Operator or Separator
 453			while ((wbo < wbl) &&
 454				(wordBuffer[wbo] != '%') &&
 455				(wordBuffer[wbo] != '!') &&
 456				(!IsBOperator(wordBuffer[wbo])) &&
 457				(!IsBSeparator(wordBuffer[wbo]))) {
 458				wbo++;
 459			}
 460			// Colorize Default Text
 461			styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT);
 462			// Reset Offset to re-process remainder of word
 463			offset -= (wbl - wbo);
 464		}
 465		// Skip next spaces - nothing happens if Offset was Reset
 466		while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
 467			offset++;
 468		}
 469	}
 470	// Colorize Default Text for remainder of line - currently not lexed
 471	styler.ColourTo(endPos, SCE_BAT_DEFAULT);
 472}
 473
 474static void ColouriseBatchDoc(
 475    unsigned int startPos,
 476    int length,
 477    int /*initStyle*/,
 478    WordList *keywordlists[],
 479    Accessor &styler) {
 480
 481	char lineBuffer[1024];
 482
 483	styler.StartAt(startPos);
 484	styler.StartSegment(startPos);
 485	unsigned int linePos = 0;
 486	unsigned int startLine = startPos;
 487	for (unsigned int i = startPos; i < startPos + length; i++) {
 488		lineBuffer[linePos++] = styler[i];
 489		if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
 490			// End of line (or of line buffer) met, colourise it
 491			lineBuffer[linePos] = '\0';
 492			ColouriseBatchLine(lineBuffer, linePos, startLine, i, keywordlists, styler);
 493			linePos = 0;
 494			startLine = i + 1;
 495		}
 496	}
 497	if (linePos > 0) {	// Last line does not have ending characters
 498		lineBuffer[linePos] = '\0';
 499		ColouriseBatchLine(lineBuffer, linePos, startLine, startPos + length - 1,
 500		                   keywordlists, styler);
 501	}
 502}
 503
 504static void ColouriseDiffLine(char *lineBuffer, int endLine, Accessor &styler) {
 505	// It is needed to remember the current state to recognize starting
 506	// comment lines before the first "diff " or "--- ". If a real
 507	// difference starts then each line starting with ' ' is a whitespace
 508	// otherwise it is considered a comment (Only in..., Binary file...)
 509	if (0 == strncmp(lineBuffer, "diff ", 5)) {
 510		styler.ColourTo(endLine, SCE_DIFF_COMMAND);
 511	} else if (0 == strncmp(lineBuffer, "Index: ", 7)) {  // For subversion's diff
 512		styler.ColourTo(endLine, SCE_DIFF_COMMAND);
 513	} else if (0 == strncmp(lineBuffer, "---", 3)) {
 514		// In a context diff, --- appears in both the header and the position markers
 515		if (lineBuffer[3] == ' ' && atoi(lineBuffer + 4) && !strchr(lineBuffer, '/'))
 516			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 517		else if (lineBuffer[3] == '\r' || lineBuffer[3] == '\n')
 518			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 519		else
 520			styler.ColourTo(endLine, SCE_DIFF_HEADER);
 521	} else if (0 == strncmp(lineBuffer, "+++ ", 4)) {
 522		// I don't know of any diff where "+++ " is a position marker, but for
 523		// consistency, do the same as with "--- " and "*** ".
 524		if (atoi(lineBuffer+4) && !strchr(lineBuffer, '/'))
 525			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 526		else
 527			styler.ColourTo(endLine, SCE_DIFF_HEADER);
 528	} else if (0 == strncmp(lineBuffer, "====", 4)) {  // For p4's diff
 529		styler.ColourTo(endLine, SCE_DIFF_HEADER);
 530	} else if (0 == strncmp(lineBuffer, "***", 3)) {
 531		// In a context diff, *** appears in both the header and the position markers.
 532		// Also ******** is a chunk header, but here it's treated as part of the
 533		// position marker since there is no separate style for a chunk header.
 534		if (lineBuffer[3] == ' ' && atoi(lineBuffer+4) && !strchr(lineBuffer, '/'))
 535			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 536		else if (lineBuffer[3] == '*')
 537			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 538		else
 539			styler.ColourTo(endLine, SCE_DIFF_HEADER);
 540	} else if (0 == strncmp(lineBuffer, "? ", 2)) {    // For difflib
 541		styler.ColourTo(endLine, SCE_DIFF_HEADER);
 542	} else if (lineBuffer[0] == '@') {
 543		styler.ColourTo(endLine, SCE_DIFF_POSITION);
 544	} else if (lineBuffer[0] >= '0' && lineBuffer[0] <= '9') {
 545		styler.ColourTo(endLine, SCE_DIFF_POSITION);
 546	} else if (lineBuffer[0] == '-' || lineBuffer[0] == '<') {
 547		styler.ColourTo(endLine, SCE_DIFF_DELETED);
 548	} else if (lineBuffer[0] == '+' || lineBuffer[0] == '>') {
 549		styler.ColourTo(endLine, SCE_DIFF_ADDED);
 550	} else if (lineBuffer[0] == '!') {
 551		styler.ColourTo(endLine, SCE_DIFF_CHANGED);
 552	} else if (lineBuffer[0] != ' ') {
 553		styler.ColourTo(endLine, SCE_DIFF_COMMENT);
 554	} else {
 555		styler.ColourTo(endLine, SCE_DIFF_DEFAULT);
 556	}
 557}
 558
 559static void ColouriseDiffDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
 560	char lineBuffer[1024];
 561	styler.StartAt(startPos);
 562	styler.StartSegment(startPos);
 563	unsigned int linePos = 0;
 564	for (unsigned int i = startPos; i < startPos + length; i++) {
 565		lineBuffer[linePos++] = styler[i];
 566		if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
 567			// End of line (or of line buffer) met, colourise it
 568			lineBuffer[linePos] = '\0';
 569			ColouriseDiffLine(lineBuffer, i, styler);
 570			linePos = 0;
 571		}
 572	}
 573	if (linePos > 0) {	// Last line does not have ending characters
 574		ColouriseDiffLine(lineBuffer, startPos + length - 1, styler);
 575	}
 576}
 577
 578static void FoldDiffDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
 579	int curLine = styler.GetLine(startPos);
 580	int curLineStart = styler.LineStart(curLine);
 581	int prevLevel = curLine > 0 ? styler.LevelAt(curLine - 1) : SC_FOLDLEVELBASE;
 582	int nextLevel;
 583
 584	do {
 585		int lineType = styler.StyleAt(curLineStart);
 586		if (lineType == SCE_DIFF_COMMAND)
 587			nextLevel = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
 588		else if (lineType == SCE_DIFF_HEADER)
 589			nextLevel = (SC_FOLDLEVELBASE + 1) | SC_FOLDLEVELHEADERFLAG;
 590		else if (lineType == SCE_DIFF_POSITION && styler[curLineStart] != '-')
 591			nextLevel = (SC_FOLDLEVELBASE + 2) | SC_FOLDLEVELHEADERFLAG;
 592		else if (prevLevel & SC_FOLDLEVELHEADERFLAG)
 593			nextLevel = (prevLevel & SC_FOLDLEVELNUMBERMASK) + 1;
 594		else
 595			nextLevel = prevLevel;
 596
 597		if ((nextLevel & SC_FOLDLEVELHEADERFLAG) && (nextLevel == prevLevel))
 598			styler.SetLevel(curLine-1, prevLevel & ~SC_FOLDLEVELHEADERFLAG);
 599
 600		styler.SetLevel(curLine, nextLevel);
 601		prevLevel = nextLevel;
 602
 603		curLineStart = styler.LineStart(++curLine);
 604	} while (static_cast<int>(startPos) + length > curLineStart);
 605}
 606
 607static void ColourisePoLine(
 608    char *lineBuffer,
 609    unsigned int lengthLine,
 610    unsigned int startLine,
 611    unsigned int endPos,
 612    Accessor &styler) {
 613
 614	unsigned int i = 0;
 615	static unsigned int state = SCE_PO_DEFAULT;
 616	unsigned int state_start = SCE_PO_DEFAULT;
 617
 618	while ((i < lengthLine) && isspacechar(lineBuffer[i]))	// Skip initial spaces
 619		i++;
 620	if (i < lengthLine) {
 621		if (lineBuffer[i] == '#') {
 622			// check if the comment contains any flags ("#, ") and
 623			// then whether the flags contain "fuzzy"
 624			if (strstart(lineBuffer, "#, ") && strstr(lineBuffer, "fuzzy"))
 625				styler.ColourTo(endPos, SCE_PO_FUZZY);
 626			else
 627				styler.ColourTo(endPos, SCE_PO_COMMENT);
 628		} else {
 629			if (lineBuffer[0] == '"') {
 630				// line continuation, use previous style
 631				styler.ColourTo(endPos, state);
 632				return;
 633			// this implicitly also matches "msgid_plural"
 634			} else if (strstart(lineBuffer, "msgid")) {
 635				state_start = SCE_PO_MSGID;
 636				state = SCE_PO_MSGID_TEXT;
 637			} else if (strstart(lineBuffer, "msgstr")) {
 638				state_start = SCE_PO_MSGSTR;
 639				state = SCE_PO_MSGSTR_TEXT;
 640			} else if (strstart(lineBuffer, "msgctxt")) {
 641				state_start = SCE_PO_MSGCTXT;
 642				state = SCE_PO_MSGCTXT_TEXT;
 643			}
 644			if (state_start != SCE_PO_DEFAULT) {
 645				// find the next space
 646				while ((i < lengthLine) && ! isspacechar(lineBuffer[i]))
 647					i++;
 648				styler.ColourTo(startLine + i - 1, state_start);
 649				styler.ColourTo(startLine + i, SCE_PO_DEFAULT);
 650				styler.ColourTo(endPos, state);
 651			}
 652		}
 653	} else {
 654		styler.ColourTo(endPos, SCE_PO_DEFAULT);
 655	}
 656}
 657
 658static void ColourisePoDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
 659	char lineBuffer[1024];
 660	styler.StartAt(startPos);
 661	styler.StartSegment(startPos);
 662	unsigned int linePos = 0;
 663	unsigned int startLine = startPos;
 664	for (unsigned int i = startPos; i < startPos + length; i++) {
 665		lineBuffer[linePos++] = styler[i];
 666		if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
 667			// End of line (or of line buffer) met, colourise it
 668			lineBuffer[linePos] = '\0';
 669			ColourisePoLine(lineBuffer, linePos, startLine, i, styler);
 670			linePos = 0;
 671			startLine = i + 1;
 672		}
 673	}
 674	if (linePos > 0) {	// Last line does not have ending characters
 675		ColourisePoLine(lineBuffer, linePos, startLine, startPos + length - 1, styler);
 676	}
 677}
 678
 679static inline bool isassignchar(unsigned char ch) {
 680	return (ch == '=') || (ch == ':');
 681}
 682
 683static void ColourisePropsLine(
 684    char *lineBuffer,
 685    unsigned int lengthLine,
 686    unsigned int startLine,
 687    unsigned int endPos,
 688    Accessor &styler,
 689    bool allowInitialSpaces) {
 690
 691	unsigned int i = 0;
 692	if (allowInitialSpaces) {
 693		while ((i < lengthLine) && isspacechar(lineBuffer[i]))	// Skip initial spaces
 694			i++;
 695	} else {
 696		if (isspacechar(lineBuffer[i])) // don't allow initial spaces
 697			i = lengthLine;
 698	}
 699
 700	if (i < lengthLine) {
 701		if (lineBuffer[i] == '#' || lineBuffer[i] == '!' || lineBuffer[i] == ';') {
 702			styler.ColourTo(endPos, SCE_PROPS_COMMENT);
 703		} else if (lineBuffer[i] == '[') {
 704			styler.ColourTo(endPos, SCE_PROPS_SECTION);
 705		} else if (lineBuffer[i] == '@') {
 706			styler.ColourTo(startLine + i, SCE_PROPS_DEFVAL);
 707			if (isassignchar(lineBuffer[i++]))
 708				styler.ColourTo(startLine + i, SCE_PROPS_ASSIGNMENT);
 709			styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
 710		} else {
 711			// Search for the '=' character
 712			while ((i < lengthLine) && !isassignchar(lineBuffer[i]))
 713				i++;
 714			if ((i < lengthLine) && isassignchar(lineBuffer[i])) {
 715				styler.ColourTo(startLine + i - 1, SCE_PROPS_KEY);
 716				styler.ColourTo(startLine + i, SCE_PROPS_ASSIGNMENT);
 717				styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
 718			} else {
 719				styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
 720			}
 721		}
 722	} else {
 723		styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
 724	}
 725}
 726
 727static void ColourisePropsDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
 728	char lineBuffer[1024];
 729	styler.StartAt(startPos);
 730	styler.StartSegment(startPos);
 731	unsigned int linePos = 0;
 732	unsigned int startLine = startPos;
 733
 734	// property lexer.props.allow.initial.spaces
 735	//	For properties files, set to 0 to style all lines that start with whitespace in the default style.
 736	//	This is not suitable for SciTE .properties files which use indentation for flow control but
 737	//	can be used for RFC2822 text where indentation is used for continuation lines.
 738	bool allowInitialSpaces = styler.GetPropertyInt("lexer.props.allow.initial.spaces", 1) != 0;
 739
 740	for (unsigned int i = startPos; i < startPos + length; i++) {
 741		lineBuffer[linePos++] = styler[i];
 742		if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
 743			// End of line (or of line buffer) met, colourise it
 744			lineBuffer[linePos] = '\0';
 745			ColourisePropsLine(lineBuffer, linePos, startLine, i, styler, allowInitialSpaces);
 746			linePos = 0;
 747			startLine = i + 1;
 748		}
 749	}
 750	if (linePos > 0) {	// Last line does not have ending characters
 751		ColourisePropsLine(lineBuffer, linePos, startLine, startPos + length - 1, styler, allowInitialSpaces);
 752	}
 753}
 754
 755// adaption by ksc, using the "} else {" trick of 1.53
 756// 030721
 757static void FoldPropsDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
 758	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
 759
 760	unsigned int endPos = startPos + length;
 761	int visibleChars = 0;
 762	int lineCurrent = styler.GetLine(startPos);
 763
 764	char chNext = styler[startPos];
 765	int styleNext = styler.StyleAt(startPos);
 766	bool headerPoint = false;
 767	int lev;
 768
 769	for (unsigned int i = startPos; i < endPos; i++) {
 770		char ch = chNext;
 771		chNext = styler[i+1];
 772
 773		int style = styleNext;
 774		styleNext = styler.StyleAt(i + 1);
 775		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
 776
 777		if (style == SCE_PROPS_SECTION) {
 778			headerPoint = true;
 779		}
 780
 781		if (atEOL) {
 782			lev = SC_FOLDLEVELBASE;
 783
 784			if (lineCurrent > 0) {
 785				int levelPrevious = styler.LevelAt(lineCurrent - 1);
 786
 787				if (levelPrevious & SC_FOLDLEVELHEADERFLAG) {
 788					lev = SC_FOLDLEVELBASE + 1;
 789				} else {
 790					lev = levelPrevious & SC_FOLDLEVELNUMBERMASK;
 791				}
 792			}
 793
 794			if (headerPoint) {
 795				lev = SC_FOLDLEVELBASE;
 796			}
 797			if (visibleChars == 0 && foldCompact)
 798				lev |= SC_FOLDLEVELWHITEFLAG;
 799
 800			if (headerPoint) {
 801				lev |= SC_FOLDLEVELHEADERFLAG;
 802			}
 803			if (lev != styler.LevelAt(lineCurrent)) {
 804				styler.SetLevel(lineCurrent, lev);
 805			}
 806
 807			lineCurrent++;
 808			visibleChars = 0;
 809			headerPoint = false;
 810		}
 811		if (!isspacechar(ch))
 812			visibleChars++;
 813	}
 814
 815	if (lineCurrent > 0) {
 816		int levelPrevious = styler.LevelAt(lineCurrent - 1);
 817		if (levelPrevious & SC_FOLDLEVELHEADERFLAG) {
 818			lev = SC_FOLDLEVELBASE + 1;
 819		} else {
 820			lev = levelPrevious & SC_FOLDLEVELNUMBERMASK;
 821		}
 822	} else {
 823		lev = SC_FOLDLEVELBASE;
 824	}
 825	int flagsNext = styler.LevelAt(lineCurrent);
 826	styler.SetLevel(lineCurrent, lev | (flagsNext & ~SC_FOLDLEVELNUMBERMASK));
 827}
 828
 829static void ColouriseMakeLine(
 830    char *lineBuffer,
 831    unsigned int lengthLine,
 832    unsigned int startLine,
 833    unsigned int endPos,
 834    Accessor &styler) {
 835
 836	unsigned int i = 0;
 837	int lastNonSpace = -1;
 838	unsigned int state = SCE_MAKE_DEFAULT;
 839	bool bSpecial = false;
 840
 841	// check for a tab character in column 0 indicating a command
 842	bool bCommand = false;
 843	if ((lengthLine > 0) && (lineBuffer[0] == '\t'))
 844		bCommand = true;
 845
 846	// Skip initial spaces
 847	while ((i < lengthLine) && isspacechar(lineBuffer[i])) {
 848		i++;
 849	}
 850	if (lineBuffer[i] == '#') {	// Comment
 851		styler.ColourTo(endPos, SCE_MAKE_COMMENT);
 852		return;
 853	}
 854	if (lineBuffer[i] == '!') {	// Special directive
 855		styler.ColourTo(endPos, SCE_MAKE_PREPROCESSOR);
 856		return;
 857	}
 858	while (i < lengthLine) {
 859		if (lineBuffer[i] == '$' && lineBuffer[i + 1] == '(') {
 860			styler.ColourTo(startLine + i - 1, state);
 861			state = SCE_MAKE_IDENTIFIER;
 862		} else if (state == SCE_MAKE_IDENTIFIER && lineBuffer[i] == ')') {
 863			styler.ColourTo(startLine + i, state);
 864			state = SCE_MAKE_DEFAULT;
 865		}
 866
 867		// skip identifier and target styling if this is a command line
 868		if (!bSpecial && !bCommand) {
 869			if (lineBuffer[i] == ':') {
 870				if (((i + 1) < lengthLine) && (lineBuffer[i + 1] == '=')) {
 871					// it's a ':=', so style as an identifier
 872					if (lastNonSpace >= 0)
 873						styler.ColourTo(startLine + lastNonSpace, SCE_MAKE_IDENTIFIER);
 874					styler.ColourTo(startLine + i - 1, SCE_MAKE_DEFAULT);
 875					styler.ColourTo(startLine + i + 1, SCE_MAKE_OPERATOR);
 876				} else {
 877					// We should check that no colouring was made since the beginning of the line,
 878					// to avoid colouring stuff like /OUT:file
 879					if (lastNonSpace >= 0)
 880						styler.ColourTo(startLine + lastNonSpace, SCE_MAKE_TARGET);
 881					styler.ColourTo(startLine + i - 1, SCE_MAKE_DEFAULT);
 882					styler.ColourTo(startLine + i, SCE_MAKE_OPERATOR);
 883				}
 884				bSpecial = true;	// Only react to the first ':' of the line
 885				state = SCE_MAKE_DEFAULT;
 886			} else if (lineBuffer[i] == '=') {
 887				if (lastNonSpace >= 0)
 888					styler.ColourTo(startLine + lastNonSpace, SCE_MAKE_IDENTIFIER);
 889				styler.ColourTo(startLine + i - 1, SCE_MAKE_DEFAULT);
 890				styler.ColourTo(startLine + i, SCE_MAKE_OPERATOR);
 891				bSpecial = true;	// Only react to the first '=' of the line
 892				state = SCE_MAKE_DEFAULT;
 893			}
 894		}
 895		if (!isspacechar(lineBuffer[i])) {
 896			lastNonSpace = i;
 897		}
 898		i++;
 899	}
 900	if (state == SCE_MAKE_IDENTIFIER) {
 901		styler.ColourTo(endPos, SCE_MAKE_IDEOL);	// Error, variable reference not ended
 902	} else {
 903		styler.ColourTo(endPos, SCE_MAKE_DEFAULT);
 904	}
 905}
 906
 907static void ColouriseMakeDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
 908	char lineBuffer[1024];
 909	styler.StartAt(startPos);
 910	styler.StartSegment(startPos);
 911	unsigned int linePos = 0;
 912	unsigned int startLine = startPos;
 913	for (unsigned int i = startPos; i < startPos + length; i++) {
 914		lineBuffer[linePos++] = styler[i];
 915		if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
 916			// End of line (or of line buffer) met, colourise it
 917			lineBuffer[linePos] = '\0';
 918			ColouriseMakeLine(lineBuffer, linePos, startLine, i, styler);
 919			linePos = 0;
 920			startLine = i + 1;
 921		}
 922	}
 923	if (linePos > 0) {	// Last line does not have ending characters
 924		ColouriseMakeLine(lineBuffer, linePos, startLine, startPos + length - 1, styler);
 925	}
 926}
 927
 928static int RecogniseErrorListLine(const char *lineBuffer, unsigned int lengthLine, int &startValue) {
 929	if (lineBuffer[0] == '>') {
 930		// Command or return status
 931		return SCE_ERR_CMD;
 932	} else if (lineBuffer[0] == '<') {
 933		// Diff removal, but not interested. Trapped to avoid hitting CTAG cases.
 934		return SCE_ERR_DEFAULT;
 935	} else if (lineBuffer[0] == '!') {
 936		return SCE_ERR_DIFF_CHANGED;
 937	} else if (lineBuffer[0] == '+') {
 938		if (strstart(lineBuffer, "+++ ")) {
 939			return SCE_ERR_DIFF_MESSAGE;
 940		} else {
 941			return SCE_ERR_DIFF_ADDITION;
 942		}
 943	} else if (lineBuffer[0] == '-') {
 944		if (strstart(lineBuffer, "--- ")) {
 945			return SCE_ERR_DIFF_MESSAGE;
 946		} else {
 947			return SCE_ERR_DIFF_DELETION;
 948		}
 949	} else if (strstart(lineBuffer, "cf90-")) {
 950		// Absoft Pro Fortran 90/95 v8.2 error and/or warning message
 951		return SCE_ERR_ABSF;
 952	} else if (strstart(lineBuffer, "fortcom:")) {
 953		// Intel Fortran Compiler v8.0 error/warning message
 954		return SCE_ERR_IFORT;
 955	} else if (strstr(lineBuffer, "File \"") && strstr(lineBuffer, ", line ")) {
 956		return SCE_ERR_PYTHON;
 957	} else if (strstr(lineBuffer, " in ") && strstr(lineBuffer, " on line ")) {
 958		return SCE_ERR_PHP;
 959	} else if ((strstart(lineBuffer, "Error ") ||
 960	            strstart(lineBuffer, "Warning ")) &&
 961	           strstr(lineBuffer, " at (") &&
 962	           strstr(lineBuffer, ") : ") &&
 963	           (strstr(lineBuffer, " at (") < strstr(lineBuffer, ") : "))) {
 964		// Intel Fortran Compiler error/warning message
 965		return SCE_ERR_IFC;
 966	} else if (strstart(lineBuffer, "Error ")) {
 967		// Borland error message
 968		return SCE_ERR_BORLAND;
 969	} else if (strstart(lineBuffer, "Warning ")) {
 970		// Borland warning message
 971		return SCE_ERR_BORLAND;
 972	} else if (strstr(lineBuffer, "at line ") &&
 973	        (strstr(lineBuffer, "at line ") < (lineBuffer + lengthLine)) &&
 974	           strstr(lineBuffer, "file ") &&
 975	           (strstr(lineBuffer, "file ") < (lineBuffer + lengthLine))) {
 976		// Lua 4 error message
 977		return SCE_ERR_LUA;
 978	} else if (strstr(lineBuffer, " at ") &&
 979	        (strstr(lineBuffer, " at ") < (lineBuffer + lengthLine)) &&
 980	           strstr(lineBuffer, " line ") &&
 981	           (strstr(lineBuffer, " line ") < (lineBuffer + lengthLine)) &&
 982	        (strstr(lineBuffer, " at ") < (strstr(lineBuffer, " line ")))) {
 983		// perl error message
 984		return SCE_ERR_PERL;
 985	} else if ((memcmp(lineBuffer, "   at ", 6) == 0) &&
 986	           strstr(lineBuffer, ":line ")) {
 987		// A .NET traceback
 988		return SCE_ERR_NET;
 989	} else if (strstart(lineBuffer, "Line ") &&
 990	           strstr(lineBuffer, ", file ")) {
 991		// Essential Lahey Fortran error message
 992		return SCE_ERR_ELF;
 993	} else if (strstart(lineBuffer, "line ") &&
 994	           strstr(lineBuffer, " column ")) {
 995		// HTML tidy style: line 42 column 1
 996		return SCE_ERR_TIDY;
 997	} else if (strstart(lineBuffer, "\tat ") &&
 998	           strstr(lineBuffer, "(") &&
 999	           strstr(lineBuffer, ".java:")) {
1000		// Java stack back trace
1001		return SCE_ERR_JAVA_STACK;
1002	} else {
1003		// Look for one of the following formats:
1004		// GCC: <filename>:<line>:<message>
1005		// Microsoft: <filename>(<line>) :<message>
1006		// Common: <filename>(<line>): warning|error|note|remark|catastrophic|fatal
1007		// Common: <filename>(<line>) warning|error|note|remark|catastrophic|fatal
1008		// Microsoft: <filename>(<line>,<column>)<message>
1009		// CTags: \t<message>
1010		// Lua 5 traceback: \t<filename>:<line>:<message>
1011		// Lua 5.1: <exe>: <filename>:<line>:<message>
1012		bool initialTab = (lineBuffer[0] == '\t');
1013		bool initialColonPart = false;
1014		enum { stInitial,
1015			stGccStart, stGccDigit, stGcc,
1016			stMsStart, stMsDigit, stMsBracket, stMsVc, stMsDigitComma, stMsDotNet,
1017			stCtagsStart, stCtagsStartString, stCtagsStringDollar, stCtags,
1018			stUnrecognized
1019		} state = stInitial;
1020		for (unsigned int i = 0; i < lengthLine; i++) {
1021			char ch = lineBuffer[i];
1022			char chNext = ' ';
1023			if ((i + 1) < lengthLine)
1024				chNext = lineBuffer[i + 1];
1025			if (state == stInitial) {
1026				if (ch == ':') {
1027					// May be GCC, or might be Lua 5 (Lua traceback same but with tab prefix)
1028					if ((chNext != '\\') && (chNext != '/') && (chNext != ' ')) {
1029						// This check is not completely accurate as may be on
1030						// GTK+ with a file name that includes ':'.
1031						state = stGccStart;
1032					} else if (chNext == ' ') { // indicates a Lua 5.1 error message
1033						initialColonPart = true;
1034					}
1035				} else if ((ch == '(') && Is1To9(chNext) && (!initialTab)) {
1036					// May be Microsoft
1037					// Check against '0' often removes phone numbers
1038					state = stMsStart;
1039				} else if ((ch == '\t') && (!initialTab)) {
1040					// May be CTags
1041					state = stCtagsStart;
1042				}
1043			} else if (state == stGccStart) {	// <filename>:
1044				state = Is1To9(ch) ? stGccDigit : stUnrecognized;
1045			} else if (state == stGccDigit) {	// <filename>:<line>
1046				if (ch == ':') {
1047					state = stGcc;	// :9.*: is GCC
1048					startValue = i + 1;
1049					break;
1050				} else if (!Is0To9(ch)) {
1051					state = stUnrecognized;
1052				}
1053			} else if (state == stMsStart) {	// <filename>(
1054				state = Is0To9(ch) ? stMsDigit : stUnrecognized;
1055			} else if (state == stMsDigit) {	// <filename>(<line>
1056				if (ch == ',') {
1057					state = stMsDigitComma;
1058				} else if (ch == ')') {
1059					state = stMsBracket;
1060				} else if ((ch != ' ') && !Is0To9(ch)) {
1061					state = stUnrecognized;
1062				}
1063			} else if (state == stMsBracket) {	// <filename>(<line>)
1064				if ((ch == ' ') && (chNext == ':')) {
1065					state = stMsVc;
1066				} else if ((ch == ':' && chNext == ' ') || (ch == ' ')) {
1067					// Possibly Delphi.. don't test against chNext as it's one of the strings below.
1068					char word[512];
1069					unsigned int j, chPos;
1070					unsigned numstep;
1071					chPos = 0;
1072					if (ch == ' ')
1073						numstep = 1; // ch was ' ', handle as if it's a delphi errorline, only add 1 to i.
1074					else
1075						numstep = 2; // otherwise add 2.
1076					for (j = i + numstep; j < lengthLine && IsAlphabetic(lineBuffer[j]) && chPos < sizeof(word) - 1; j++)
1077						word[chPos++] = lineBuffer[j];
1078					word[chPos] = 0;
1079					if (!CompareCaseInsensitive(word, "error") || !CompareCaseInsensitive(word, "warning") ||
1080						!CompareCaseInsensitive(word, "fatal") || !CompareCaseInsensitive(word, "catastrophic") ||
1081						!CompareCaseInsensitive(word, "note") || !CompareCaseInsensitive(word, "remark")) {
1082						state = stMsVc;
1083					} else
1084						state = stUnrecognized;
1085				} else {
1086					state = stUnrecognized;
1087				}
1088			} else if (state == stMsDigitComma) {	// <filename>(<line>,
1089				if (ch == ')') {
1090					state = stMsDotNet;
1091					break;
1092				} else if ((ch != ' ') && !Is0To9(ch)) {
1093					state = stUnrecognized;
1094				}
1095			} else if (state == stCtagsStart) {
1096				if ((lineBuffer[i - 1] == '\t') &&
1097				        ((ch == '/' && lineBuffer[i + 1] == '^') || Is0To9(ch))) {
1098					state = stCtags;
1099					break;
1100				} else if ((ch == '/') && (lineBuffer[i + 1] == '^')) {
1101					state = stCtagsStartString;
1102				}
1103			} else if ((state == stCtagsStartString) && ((lineBuffer[i] == '$') && (lineBuffer[i + 1] == '/'))) {
1104				state = stCtagsStringDollar;
1105				break;
1106			}
1107		}
1108		if (state == stGcc) {
1109			return initialColonPart ? SCE_ERR_LUA : SCE_ERR_GCC;
1110		} else if ((state == stMsVc) || (state == stMsDotNet)) {
1111			return SCE_ERR_MS;
1112		} else if ((state == stCtagsStringDollar) || (state == stCtags)) {
1113			return SCE_ERR_CTAG;
1114		} else {
1115			return SCE_ERR_DEFAULT;
1116		}
1117	}
1118}
1119
1120static void ColouriseErrorListLine(
1121    char *lineBuffer,
1122    unsigned int lengthLine,
1123    unsigned int endPos,
1124    Accessor &styler,
1125	bool valueSeparate) {
1126	int startValue = -1;
1127	int style = RecogniseErrorListLine(lineBuffer, lengthLine, startValue);
1128	if (valueSeparate && (startValue >= 0)) {
1129		styler.ColourTo(endPos - (lengthLine - startValue), style);
1130		styler.ColourTo(endPos, SCE_ERR_VALUE);
1131	} else {
1132		styler.ColourTo(endPos, style);
1133	}
1134}
1135
1136static void ColouriseErrorListDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
1137	char lineBuffer[10000];
1138	styler.StartAt(startPos);
1139	styler.StartSegment(startPos);
1140	unsigned int linePos = 0;
1141
1142	// property lexer.errorlist.value.separate
1143	//	For lines in the output pane that are matches from Find in Files or GCC-style
1144	//	diagnostics, style the path and line number separately from the rest of the
1145	//	line with style 21 used for the rest of the line.
1146	//	This allows matched text to be more easily distinguished from its location.
1147	bool valueSeparate = styler.GetPropertyInt("lexer.errorlist.value.separate", 0) != 0;
1148	for (unsigned int i = startPos; i < startPos + length; i++) {
1149		lineBuffer[linePos++] = styler[i];
1150		if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
1151			// End of line (or of line buffer) met, colourise it
1152			lineBuffer[linePos] = '\0';
1153			ColouriseErrorListLine(lineBuffer, linePos, i, styler, valueSeparate);
1154			linePos = 0;
1155		}
1156	}
1157	if (linePos > 0) {	// Last line does not have ending characters
1158		ColouriseErrorListLine(lineBuffer, linePos, startPos + length - 1, styler, valueSeparate);
1159	}
1160}
1161
1162static int isSpecial(char s) {
1163	return (s == '\\') || (s == ',') || (s == ';') || (s == '\'') || (s == ' ') ||
1164	       (s == '\"') || (s == '`') || (s == '^') || (s == '~');
1165}
1166
1167static int isTag(int start, Accessor &styler) {
1168	char s[6];
1169	unsigned int i = 0, e = 1;
1170	while (i < 5 && e) {
1171		s[i] = styler[start + i];
1172		i++;
1173		e = (strchr("{ \t", styler[start + i]) == NULL);
1174	}
1175	s[i] = '\0';
1176	return (strcmp(s, "begin") == 0) || (strcmp(s, "end") == 0);
1177}
1178
1179static void ColouriseLatexDoc(unsigned int startPos, int length, int initStyle,
1180                              WordList *[], Accessor &styler) {
1181
1182	styler.StartAt(startPos);
1183
1184	int state = initStyle;
1185	char chNext = styler[startPos];
1186	styler.StartSegment(startPos);
1187	int lengthDoc = startPos + length;
1188
1189	for (int i = startPos; i < lengthDoc; i++) {
1190		char ch = chNext;
1191		chNext = styler.SafeGetCharAt(i + 1);
1192
1193		if (styler.IsLeadByte(ch)) {
1194			chNext = styler.SafeGetCharAt(i + 2);
1195			i++;
1196			continue;
1197		}
1198		switch (state) {
1199		case SCE_L_DEFAULT :
1200			switch (ch) {
1201			case '\\' :
1202				styler.ColourTo(i - 1, state);
1203				if (isSpecial(styler[i + 1])) {
1204					styler.ColourTo(i + 1, SCE_L_COMMAND);
1205					i++;
1206					chNext = styler.SafeGetCharAt(i + 1);
1207				} else {
1208					if (isTag(i + 1, styler))
1209						state = SCE_L_TAG;
1210					else
1211						state = SCE_L_COMMAND;
1212				}
1213				break;
1214			case '$' :
1215				styler.ColourTo(i - 1, state);
1216				state = SCE_L_MATH;
1217				if (chNext == '$') {
1218					i++;
1219					chNext = styler.SafeGetCharAt(i + 1);
1220				}
1221				break;
1222			case '%' :
1223				styler.ColourTo(i - 1, state);
1224				state = SCE_L_COMMENT;
1225				break;
1226			}
1227			break;
1228		case SCE_L_COMMAND :
1229			if (chNext == '[' || chNext == '{' || chNext == '}' ||
1230			        chNext == ' ' || chNext == '\r' || chNext == '\n') {
1231				styler.ColourTo(i, state);
1232				state = SCE_L_DEFAULT;
1233				i++;
1234				chNext = styler.SafeGetCharAt(i + 1);
1235			}
1236			break;
1237		case SCE_L_TAG :
1238			if (ch == '}') {
1239				styler.ColourTo(i, state);
1240				state = SCE_L_DEFAULT;
1241			}
1242			break;
1243		case SCE_L_MATH :
1244			if (ch == '$') {
1245				if (chNext == '$') {
1246					i++;
1247					chNext = styler.SafeGetCharAt(i + 1);
1248				}
1249				styler.ColourTo(i, state);
1250				state = SCE_L_DEFAULT;
1251			}
1252			break;
1253		case SCE_L_COMMENT :
1254			if (ch == '\r' || ch == '\n') {
1255				styler.ColourTo(i - 1, state);
1256				state = SCE_L_DEFAULT;
1257			}
1258		}
1259	}
1260	styler.ColourTo(lengthDoc-1, state);
1261}
1262
1263static const char *const batchWordListDesc[] = {
1264	"Internal Commands",
1265	"External Commands",
1266	0
1267};
1268
1269static const char *const emptyWordListDesc[] = {
1270	0
1271};
1272
1273static void ColouriseNullDoc(unsigned int startPos, int length, int, WordList *[],
1274                            Accessor &styler) {
1275	// Null language means all style bytes are 0 so just mark the end - no need to fill in.
1276	if (length > 0) {
1277		styler.StartAt(startPos + length - 1);
1278		styler.StartSegment(startPos + length - 1);
1279		styler.ColourTo(startPos + length - 1, 0);
1280	}
1281}
1282
1283LexerModule lmBatch(SCLEX_BATCH, ColouriseBatchDoc, "batch", 0, batchWordListDesc);
1284LexerModule lmDiff(SCLEX_DIFF, ColouriseDiffDoc, "diff", FoldDiffDoc, emptyWordListDesc);
1285LexerModule lmPo(SCLEX_PO, ColourisePoDoc, "po", 0, emptyWordListDesc);
1286LexerModule lmProps(SCLEX_PROPERTIES, ColourisePropsDoc, "props", FoldPropsDoc, emptyWordListDesc);
1287LexerModule lmMake(SCLEX_MAKEFILE, ColouriseMakeDoc, "makefile", 0, emptyWordListDesc);
1288LexerModule lmErrorList(SCLEX_ERRORLIST, ColouriseErrorListDoc, "errorlist", 0, emptyWordListDesc);
1289LexerModule lmLatex(SCLEX_LATEX, ColouriseLatexDoc, "latex", 0, emptyWordListDesc);
1290LexerModule lmNull(SCLEX_NULL, ColouriseNullDoc, "null");