PageRenderTime 2328ms CodeModel.GetById 393ms app.highlight 1219ms RepoModel.GetById 372ms app.codeStats 9ms

/Pythonwin/Scintilla/src/LexOthers.cxx

https://bitbucket.org/jaraco/pywin32
C++ | 1231 lines | 1068 code | 31 blank | 132 comment | 262 complexity | 482d05461ce8338ab77df49ff6c0a50f 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 <ctype.h>
  12#include <stdio.h>
  13#include <stdarg.h>
  14
  15#include "Platform.h"
  16
  17#include "PropSet.h"
  18#include "Accessor.h"
  19#include "KeyWords.h"
  20#include "Scintilla.h"
  21#include "SciLexer.h"
  22
  23#ifdef SCI_NAMESPACE
  24using namespace Scintilla;
  25#endif
  26
  27static bool strstart(const char *haystack, const char *needle) {
  28	return strncmp(haystack, needle, strlen(needle)) == 0;
  29}
  30
  31static bool Is0To9(char ch) {
  32	return (ch >= '0') && (ch <= '9');
  33}
  34
  35static bool Is1To9(char ch) {
  36	return (ch >= '1') && (ch <= '9');
  37}
  38
  39static inline bool AtEOL(Accessor &styler, unsigned int i) {
  40	return (styler[i] == '\n') ||
  41	       ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
  42}
  43
  44// Tests for BATCH Operators
  45static bool IsBOperator(char ch) {
  46	return (ch == '=') || (ch == '+') || (ch == '>') || (ch == '<') ||
  47		(ch == '|') || (ch == '?') || (ch == '*');
  48}
  49
  50// Tests for BATCH Separators
  51static bool IsBSeparator(char ch) {
  52	return (ch == '\\') || (ch == '.') || (ch == ';') ||
  53		(ch == '\"') || (ch == '\'') || (ch == '/') || (ch == ')');
  54}
  55
  56static void ColouriseBatchLine(
  57    char *lineBuffer,
  58    unsigned int lengthLine,
  59    unsigned int startLine,
  60    unsigned int endPos,
  61    WordList *keywordlists[],
  62    Accessor &styler) {
  63
  64	unsigned int offset = 0;	// Line Buffer Offset
  65	unsigned int cmdLoc;		// External Command / Program Location
  66	char wordBuffer[81];		// Word Buffer - large to catch long paths
  67	unsigned int wbl;		// Word Buffer Length
  68	unsigned int wbo;		// Word Buffer Offset - also Special Keyword Buffer Length
  69	WordList &keywords = *keywordlists[0];      // Internal Commands
  70	WordList &keywords2 = *keywordlists[1];     // External Commands (optional)
  71
  72	// CHOICE, ECHO, GOTO, PROMPT and SET have Default Text that may contain Regular Keywords
  73	//   Toggling Regular Keyword Checking off improves readability
  74	// Other Regular Keywords and External Commands / Programs might also benefit from toggling
  75	//   Need a more robust algorithm to properly toggle Regular Keyword Checking
  76	bool continueProcessing = true;	// Used to toggle Regular Keyword Checking
  77	// Special Keywords are those that allow certain characters without whitespace after the command
  78	// Examples are: cd. cd\ md. rd. dir| dir> echo: echo. path=
  79	// Special Keyword Buffer used to determine if the first n characters is a Keyword
  80	char sKeywordBuffer[10];	// Special Keyword Buffer
  81	bool sKeywordFound;		// Exit Special Keyword for-loop if found
  82
  83	// Skip initial spaces
  84	while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
  85		offset++;
  86	}
  87	// Colorize Default Text
  88	styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
  89	// Set External Command / Program Location
  90	cmdLoc = offset;
  91
  92	// Check for Fake Label (Comment) or Real Label - return if found
  93	if (lineBuffer[offset] == ':') {
  94		if (lineBuffer[offset + 1] == ':') {
  95			// Colorize Fake Label (Comment) - :: is similar to REM, see http://content.techweb.com/winmag/columns/explorer/2000/21.htm
  96			styler.ColourTo(endPos, SCE_BAT_COMMENT);
  97		} else {
  98			// Colorize Real Label
  99			styler.ColourTo(endPos, SCE_BAT_LABEL);
 100		}
 101		return;
 102	// Check for Drive Change (Drive Change is internal command) - return if found
 103	} else if ((isalpha(lineBuffer[offset])) &&
 104		(lineBuffer[offset + 1] == ':') &&
 105		((isspacechar(lineBuffer[offset + 2])) ||
 106		(((lineBuffer[offset + 2] == '\\')) &&
 107		(isspacechar(lineBuffer[offset + 3]))))) {
 108		// Colorize Regular Keyword
 109		styler.ColourTo(endPos, SCE_BAT_WORD);
 110		return;
 111	}
 112
 113	// Check for Hide Command (@ECHO OFF/ON)
 114	if (lineBuffer[offset] == '@') {
 115		styler.ColourTo(startLine + offset, SCE_BAT_HIDE);
 116		offset++;
 117	}
 118	// Skip next spaces
 119	while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
 120		offset++;
 121	}
 122
 123	// Read remainder of line word-at-a-time or remainder-of-word-at-a-time
 124	while (offset < lengthLine) {
 125		if (offset > startLine) {
 126			// Colorize Default Text
 127			styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
 128		}
 129		// Copy word from Line Buffer into Word Buffer
 130		wbl = 0;
 131		for (; offset < lengthLine && wbl < 80 &&
 132		        !isspacechar(lineBuffer[offset]); wbl++, offset++) {
 133			wordBuffer[wbl] = static_cast<char>(tolower(lineBuffer[offset]));
 134		}
 135		wordBuffer[wbl] = '\0';
 136		wbo = 0;
 137
 138		// Check for Comment - return if found
 139		if (CompareCaseInsensitive(wordBuffer, "rem") == 0) {
 140			styler.ColourTo(endPos, SCE_BAT_COMMENT);
 141			return;
 142		}
 143		// Check for Separator
 144		if (IsBSeparator(wordBuffer[0])) {
 145			// Check for External Command / Program
 146			if ((cmdLoc == offset - wbl) &&
 147				((wordBuffer[0] == ':') ||
 148				(wordBuffer[0] == '\\') ||
 149				(wordBuffer[0] == '.'))) {
 150				// Reset Offset to re-process remainder of word
 151				offset -= (wbl - 1);
 152				// Colorize External Command / Program
 153				if (!keywords2) {
 154					styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
 155				} else if (keywords2.InList(wordBuffer)) {
 156					styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
 157				} else {
 158					styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
 159				}
 160				// Reset External Command / Program Location
 161				cmdLoc = offset;
 162			} else {
 163				// Reset Offset to re-process remainder of word
 164				offset -= (wbl - 1);
 165				// Colorize Default Text
 166				styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
 167			}
 168		// Check for Regular Keyword in list
 169		} else if ((keywords.InList(wordBuffer)) &&
 170			(continueProcessing)) {
 171			// ECHO, GOTO, PROMPT and SET require no further Regular Keyword Checking
 172			if ((CompareCaseInsensitive(wordBuffer, "echo") == 0) ||
 173				(CompareCaseInsensitive(wordBuffer, "goto") == 0) ||
 174				(CompareCaseInsensitive(wordBuffer, "prompt") == 0) ||
 175				(CompareCaseInsensitive(wordBuffer, "set") == 0)) {
 176				continueProcessing = false;
 177			}
 178			// Identify External Command / Program Location for ERRORLEVEL, and EXIST
 179			if ((CompareCaseInsensitive(wordBuffer, "errorlevel") == 0) ||
 180				(CompareCaseInsensitive(wordBuffer, "exist") == 0)) {
 181				// Reset External Command / Program Location
 182				cmdLoc = offset;
 183				// Skip next spaces
 184				while ((cmdLoc < lengthLine) &&
 185					(isspacechar(lineBuffer[cmdLoc]))) {
 186					cmdLoc++;
 187				}
 188				// Skip comparison
 189				while ((cmdLoc < lengthLine) &&
 190					(!isspacechar(lineBuffer[cmdLoc]))) {
 191					cmdLoc++;
 192				}
 193				// Skip next spaces
 194				while ((cmdLoc < lengthLine) &&
 195					(isspacechar(lineBuffer[cmdLoc]))) {
 196					cmdLoc++;
 197				}
 198			// Identify External Command / Program Location for CALL, DO, LOADHIGH and LH
 199			} else if ((CompareCaseInsensitive(wordBuffer, "call") == 0) ||
 200				(CompareCaseInsensitive(wordBuffer, "do") == 0) ||
 201				(CompareCaseInsensitive(wordBuffer, "loadhigh") == 0) ||
 202				(CompareCaseInsensitive(wordBuffer, "lh") == 0)) {
 203				// Reset External Command / Program Location
 204				cmdLoc = offset;
 205				// Skip next spaces
 206				while ((cmdLoc < lengthLine) &&
 207					(isspacechar(lineBuffer[cmdLoc]))) {
 208					cmdLoc++;
 209				}
 210			}
 211			// Colorize Regular keyword
 212			styler.ColourTo(startLine + offset - 1, SCE_BAT_WORD);
 213			// No need to Reset Offset
 214		// Check for Special Keyword in list, External Command / Program, or Default Text
 215		} else if ((wordBuffer[0] != '%') &&
 216			(!IsBOperator(wordBuffer[0])) &&
 217			(continueProcessing)) {
 218			// Check for Special Keyword
 219			//     Affected Commands are in Length range 2-6
 220			//     Good that ERRORLEVEL, EXIST, CALL, DO, LOADHIGH, and LH are unaffected
 221			sKeywordFound = false;
 222			for (unsigned int keywordLength = 2; keywordLength < wbl && keywordLength < 7 && !sKeywordFound; keywordLength++) {
 223				wbo = 0;
 224				// Copy Keyword Length from Word Buffer into Special Keyword Buffer
 225				for (; wbo < keywordLength; wbo++) {
 226					sKeywordBuffer[wbo] = static_cast<char>(wordBuffer[wbo]);
 227				}
 228				sKeywordBuffer[wbo] = '\0';
 229				// Check for Special Keyword in list
 230				if ((keywords.InList(sKeywordBuffer)) &&
 231					((IsBOperator(wordBuffer[wbo])) ||
 232					(IsBSeparator(wordBuffer[wbo])))) {
 233					sKeywordFound = true;
 234					// ECHO requires no further Regular Keyword Checking
 235					if (CompareCaseInsensitive(sKeywordBuffer, "echo") == 0) {
 236						continueProcessing = false;
 237					}
 238					// Colorize Special Keyword as Regular Keyword
 239					styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_WORD);
 240					// Reset Offset to re-process remainder of word
 241					offset -= (wbl - wbo);
 242				}
 243			}
 244			// Check for External Command / Program or Default Text
 245			if (!sKeywordFound) {
 246				wbo = 0;
 247				// Check for External Command / Program
 248				if (cmdLoc == offset - wbl) {
 249					// Read up to %, Operator or Separator
 250					while ((wbo < wbl) &&
 251						(wordBuffer[wbo] != '%') &&
 252						(!IsBOperator(wordBuffer[wbo])) &&
 253						(!IsBSeparator(wordBuffer[wbo]))) {
 254						wbo++;
 255					}
 256					// Reset External Command / Program Location
 257					cmdLoc = offset - (wbl - wbo);
 258					// Reset Offset to re-process remainder of word
 259					offset -= (wbl - wbo);
 260					// CHOICE requires no further Regular Keyword Checking
 261					if (CompareCaseInsensitive(wordBuffer, "choice") == 0) {
 262						continueProcessing = false;
 263					}
 264					// Check for START (and its switches) - What follows is External Command \ Program
 265					if (CompareCaseInsensitive(wordBuffer, "start") == 0) {
 266						// Reset External Command / Program Location
 267						cmdLoc = offset;
 268						// Skip next spaces
 269						while ((cmdLoc < lengthLine) &&
 270							(isspacechar(lineBuffer[cmdLoc]))) {
 271							cmdLoc++;
 272						}
 273						// Reset External Command / Program Location if command switch detected
 274						if (lineBuffer[cmdLoc] == '/') {
 275							// Skip command switch
 276							while ((cmdLoc < lengthLine) &&
 277								(!isspacechar(lineBuffer[cmdLoc]))) {
 278								cmdLoc++;
 279							}
 280							// Skip next spaces
 281							while ((cmdLoc < lengthLine) &&
 282								(isspacechar(lineBuffer[cmdLoc]))) {
 283								cmdLoc++;
 284							}
 285						}
 286					}
 287					// Colorize External Command / Program
 288					if (!keywords2) {
 289						styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
 290					} else if (keywords2.InList(wordBuffer)) {
 291						styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
 292					} else {
 293						styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
 294					}
 295					// No need to Reset Offset
 296				// Check for Default Text
 297				} else {
 298					// Read up to %, Operator or Separator
 299					while ((wbo < wbl) &&
 300						(wordBuffer[wbo] != '%') &&
 301						(!IsBOperator(wordBuffer[wbo])) &&
 302						(!IsBSeparator(wordBuffer[wbo]))) {
 303						wbo++;
 304					}
 305					// Colorize Default Text
 306					styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT);
 307					// Reset Offset to re-process remainder of word
 308					offset -= (wbl - wbo);
 309				}
 310			}
 311		// Check for Argument  (%n), Environment Variable (%x...%) or Local Variable (%%a)
 312		} else if (wordBuffer[0] == '%') {
 313			// Colorize Default Text
 314			styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT);
 315			wbo++;
 316			// Search to end of word for second % (can be a long path)
 317			while ((wbo < wbl) &&
 318				(wordBuffer[wbo] != '%') &&
 319				(!IsBOperator(wordBuffer[wbo])) &&
 320				(!IsBSeparator(wordBuffer[wbo]))) {
 321				wbo++;
 322			}
 323			// Check for Argument (%n) or (%*)
 324			if (((Is0To9(wordBuffer[1])) || (wordBuffer[1] == '*')) &&
 325				(wordBuffer[wbo] != '%')) {
 326				// Check for External Command / Program
 327				if (cmdLoc == offset - wbl) {
 328					cmdLoc = offset - (wbl - 2);
 329				}
 330				// Colorize Argument
 331				styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_IDENTIFIER);
 332				// Reset Offset to re-process remainder of word
 333				offset -= (wbl - 2);
 334			// Check for Expanded Argument (%~...) / Variable (%%~...)
 335			} else if (((wbl > 1) && (wordBuffer[1] == '~')) ||
 336				((wbl > 2) && (wordBuffer[1] == '%') && (wordBuffer[2] == '~'))) {
 337				// Check for External Command / Program
 338				if (cmdLoc == offset - wbl) {
 339					cmdLoc = offset - (wbl - wbo);
 340				}
 341				// Colorize Expanded Argument / Variable
 342				styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
 343				// Reset Offset to re-process remainder of word
 344				offset -= (wbl - wbo);
 345			// Check for Environment Variable (%x...%)
 346			} else if ((wordBuffer[1] != '%') &&
 347				(wordBuffer[wbo] == '%')) {
 348				wbo++;
 349				// Check for External Command / Program
 350				if (cmdLoc == offset - wbl) {
 351					cmdLoc = offset - (wbl - wbo);
 352				}
 353				// Colorize Environment 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 Local Variable (%%a)
 358			} else if (
 359				(wbl > 2) &&
 360				(wordBuffer[1] == '%') &&
 361				(wordBuffer[2] != '%') &&
 362				(!IsBOperator(wordBuffer[2])) &&
 363				(!IsBSeparator(wordBuffer[2]))) {
 364				// Check for External Command / Program
 365				if (cmdLoc == offset - wbl) {
 366					cmdLoc = offset - (wbl - 3);
 367				}
 368				// Colorize Local Variable
 369				styler.ColourTo(startLine + offset - 1 - (wbl - 3), SCE_BAT_IDENTIFIER);
 370				// Reset Offset to re-process remainder of word
 371				offset -= (wbl - 3);
 372			}
 373		// Check for Operator
 374		} else if (IsBOperator(wordBuffer[0])) {
 375			// Colorize Default Text
 376			styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT);
 377			// Check for Comparison Operator
 378			if ((wordBuffer[0] == '=') && (wordBuffer[1] == '=')) {
 379				// Identify External Command / Program Location for IF
 380				cmdLoc = offset;
 381				// Skip next spaces
 382				while ((cmdLoc < lengthLine) &&
 383					(isspacechar(lineBuffer[cmdLoc]))) {
 384					cmdLoc++;
 385				}
 386				// Colorize Comparison Operator
 387				styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_OPERATOR);
 388				// Reset Offset to re-process remainder of word
 389				offset -= (wbl - 2);
 390			// Check for Pipe Operator
 391			} else if (wordBuffer[0] == '|') {
 392				// Reset External Command / Program Location
 393				cmdLoc = offset - wbl + 1;
 394				// Skip next spaces
 395				while ((cmdLoc < lengthLine) &&
 396					(isspacechar(lineBuffer[cmdLoc]))) {
 397					cmdLoc++;
 398				}
 399				// Colorize Pipe Operator
 400				styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_OPERATOR);
 401				// Reset Offset to re-process remainder of word
 402				offset -= (wbl - 1);
 403			// Check for Other Operator
 404			} else {
 405				// Check for > Operator
 406				if (wordBuffer[0] == '>') {
 407					// Turn Keyword and External Command / Program checking back on
 408					continueProcessing = true;
 409				}
 410				// Colorize Other Operator
 411				styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_OPERATOR);
 412				// Reset Offset to re-process remainder of word
 413				offset -= (wbl - 1);
 414			}
 415		// Check for Default Text
 416		} else {
 417			// Read up to %, Operator or Separator
 418			while ((wbo < wbl) &&
 419				(wordBuffer[wbo] != '%') &&
 420				(!IsBOperator(wordBuffer[wbo])) &&
 421				(!IsBSeparator(wordBuffer[wbo]))) {
 422				wbo++;
 423			}
 424			// Colorize Default Text
 425			styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT);
 426			// Reset Offset to re-process remainder of word
 427			offset -= (wbl - wbo);
 428		}
 429		// Skip next spaces - nothing happens if Offset was Reset
 430		while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
 431			offset++;
 432		}
 433	}
 434	// Colorize Default Text for remainder of line - currently not lexed
 435	styler.ColourTo(endPos, SCE_BAT_DEFAULT);
 436}
 437
 438static void ColouriseBatchDoc(
 439    unsigned int startPos,
 440    int length,
 441    int /*initStyle*/,
 442    WordList *keywordlists[],
 443    Accessor &styler) {
 444
 445	char lineBuffer[1024];
 446
 447	styler.StartAt(startPos);
 448	styler.StartSegment(startPos);
 449	unsigned int linePos = 0;
 450	unsigned int startLine = startPos;
 451	for (unsigned int i = startPos; i < startPos + length; i++) {
 452		lineBuffer[linePos++] = styler[i];
 453		if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
 454			// End of line (or of line buffer) met, colourise it
 455			lineBuffer[linePos] = '\0';
 456			ColouriseBatchLine(lineBuffer, linePos, startLine, i, keywordlists, styler);
 457			linePos = 0;
 458			startLine = i + 1;
 459		}
 460	}
 461	if (linePos > 0) {	// Last line does not have ending characters
 462		lineBuffer[linePos] = '\0';
 463		ColouriseBatchLine(lineBuffer, linePos, startLine, startPos + length - 1,
 464		                   keywordlists, styler);
 465	}
 466}
 467
 468static void ColouriseDiffLine(char *lineBuffer, int endLine, Accessor &styler) {
 469	// It is needed to remember the current state to recognize starting
 470	// comment lines before the first "diff " or "--- ". If a real
 471	// difference starts then each line starting with ' ' is a whitespace
 472	// otherwise it is considered a comment (Only in..., Binary file...)
 473	if (0 == strncmp(lineBuffer, "diff ", 5)) {
 474		styler.ColourTo(endLine, SCE_DIFF_COMMAND);
 475	} else if (0 == strncmp(lineBuffer, "Index: ", 7)) {  // For subversion's diff
 476		styler.ColourTo(endLine, SCE_DIFF_COMMAND);
 477	} else if (0 == strncmp(lineBuffer, "---", 3)) {
 478		// In a context diff, --- appears in both the header and the position markers
 479		if (lineBuffer[3] == ' ' && atoi(lineBuffer + 4) && !strchr(lineBuffer, '/'))
 480			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 481		else if (lineBuffer[3] == '\r' || lineBuffer[3] == '\n')
 482			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 483		else
 484			styler.ColourTo(endLine, SCE_DIFF_HEADER);
 485	} else if (0 == strncmp(lineBuffer, "+++ ", 4)) {
 486		// I don't know of any diff where "+++ " is a position marker, but for
 487		// consistency, do the same as with "--- " and "*** ".
 488		if (atoi(lineBuffer+4) && !strchr(lineBuffer, '/'))
 489			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 490		else
 491			styler.ColourTo(endLine, SCE_DIFF_HEADER);
 492	} else if (0 == strncmp(lineBuffer, "====", 4)) {  // For p4's diff
 493		styler.ColourTo(endLine, SCE_DIFF_HEADER);
 494	} else if (0 == strncmp(lineBuffer, "***", 3)) {
 495		// In a context diff, *** appears in both the header and the position markers.
 496		// Also ******** is a chunk header, but here it's treated as part of the
 497		// position marker since there is no separate style for a chunk header.
 498		if (lineBuffer[3] == ' ' && atoi(lineBuffer+4) && !strchr(lineBuffer, '/'))
 499			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 500		else if (lineBuffer[3] == '*')
 501			styler.ColourTo(endLine, SCE_DIFF_POSITION);
 502		else
 503			styler.ColourTo(endLine, SCE_DIFF_HEADER);
 504	} else if (0 == strncmp(lineBuffer, "? ", 2)) {    // For difflib
 505		styler.ColourTo(endLine, SCE_DIFF_HEADER);
 506	} else if (lineBuffer[0] == '@') {
 507		styler.ColourTo(endLine, SCE_DIFF_POSITION);
 508	} else if (lineBuffer[0] >= '0' && lineBuffer[0] <= '9') {
 509		styler.ColourTo(endLine, SCE_DIFF_POSITION);
 510	} else if (lineBuffer[0] == '-' || lineBuffer[0] == '<') {
 511		styler.ColourTo(endLine, SCE_DIFF_DELETED);
 512	} else if (lineBuffer[0] == '+' || lineBuffer[0] == '>') {
 513		styler.ColourTo(endLine, SCE_DIFF_ADDED);
 514	} else if (lineBuffer[0] == '!') {
 515		styler.ColourTo(endLine, SCE_DIFF_CHANGED);
 516	} else if (lineBuffer[0] != ' ') {
 517		styler.ColourTo(endLine, SCE_DIFF_COMMENT);
 518	} else {
 519		styler.ColourTo(endLine, SCE_DIFF_DEFAULT);
 520	}
 521}
 522
 523static void ColouriseDiffDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
 524	char lineBuffer[1024];
 525	styler.StartAt(startPos);
 526	styler.StartSegment(startPos);
 527	unsigned int linePos = 0;
 528	for (unsigned int i = startPos; i < startPos + length; i++) {
 529		lineBuffer[linePos++] = styler[i];
 530		if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
 531			// End of line (or of line buffer) met, colourise it
 532			lineBuffer[linePos] = '\0';
 533			ColouriseDiffLine(lineBuffer, i, styler);
 534			linePos = 0;
 535		}
 536	}
 537	if (linePos > 0) {	// Last line does not have ending characters
 538		ColouriseDiffLine(lineBuffer, startPos + length - 1, styler);
 539	}
 540}
 541
 542static void FoldDiffDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
 543	int curLine = styler.GetLine(startPos);
 544	int curLineStart = styler.LineStart(curLine);
 545	int prevLevel = curLine > 0 ? styler.LevelAt(curLine - 1) : SC_FOLDLEVELBASE;
 546	int nextLevel;
 547
 548	do {
 549		int lineType = styler.StyleAt(curLineStart);
 550		if (lineType == SCE_DIFF_COMMAND)
 551			nextLevel = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
 552		else if (lineType == SCE_DIFF_HEADER)
 553			nextLevel = (SC_FOLDLEVELBASE + 1) | SC_FOLDLEVELHEADERFLAG;
 554		else if (lineType == SCE_DIFF_POSITION && styler[curLineStart] != '-')
 555			nextLevel = (SC_FOLDLEVELBASE + 2) | SC_FOLDLEVELHEADERFLAG;
 556		else if (prevLevel & SC_FOLDLEVELHEADERFLAG)
 557			nextLevel = (prevLevel & SC_FOLDLEVELNUMBERMASK) + 1;
 558		else
 559			nextLevel = prevLevel;
 560
 561		if ((nextLevel & SC_FOLDLEVELHEADERFLAG) && (nextLevel == prevLevel))
 562			styler.SetLevel(curLine-1, prevLevel & ~SC_FOLDLEVELHEADERFLAG);
 563
 564		styler.SetLevel(curLine, nextLevel);
 565		prevLevel = nextLevel;
 566
 567		curLineStart = styler.LineStart(++curLine);
 568	} while (static_cast<int>(startPos) + length > curLineStart);
 569}
 570
 571static void ColourisePoLine(
 572    char *lineBuffer,
 573    unsigned int lengthLine,
 574    unsigned int startLine,
 575    unsigned int endPos,
 576    Accessor &styler) {
 577
 578	unsigned int i = 0;
 579	static unsigned int state = SCE_PO_DEFAULT;
 580	unsigned int state_start = SCE_PO_DEFAULT;
 581
 582	while ((i < lengthLine) && isspacechar(lineBuffer[i]))	// Skip initial spaces
 583		i++;
 584	if (i < lengthLine) {
 585		if (lineBuffer[i] == '#') {
 586			// check if the comment contains any flags ("#, ") and
 587			// then whether the flags contain "fuzzy"
 588			if (strstart(lineBuffer, "#, ") && strstr(lineBuffer, "fuzzy"))
 589				styler.ColourTo(endPos, SCE_PO_FUZZY);
 590			else
 591				styler.ColourTo(endPos, SCE_PO_COMMENT);
 592		} else {
 593			if (lineBuffer[0] == '"') {
 594				// line continuation, use previous style
 595				styler.ColourTo(endPos, state);
 596				return;
 597			// this implicitly also matches "msgid_plural"
 598			} else if (strstart(lineBuffer, "msgid")) {
 599				state_start = SCE_PO_MSGID;
 600				state = SCE_PO_MSGID_TEXT;
 601			} else if (strstart(lineBuffer, "msgstr")) {
 602				state_start = SCE_PO_MSGSTR;
 603				state = SCE_PO_MSGSTR_TEXT;
 604			} else if (strstart(lineBuffer, "msgctxt")) {
 605				state_start = SCE_PO_MSGCTXT;
 606				state = SCE_PO_MSGCTXT_TEXT;
 607			}
 60