mirror of
				https://github.com/notepad-plus-plus/notepad-plus-plus.git
				synced 2025-10-31 03:24:04 +01:00 
			
		
		
		
	Update with https://www.scintilla.org/scintilla521.zip https://www.scintilla.org/lexilla515.zip - fix setting to bring Scintilla::PositionCR from ScintillaStructures.h inline with Sci_Position.h Sci_PositionCR - add workaround to enable lexer for searchResult commented out SCI_SETILEXER call on searchResult to get one result which is correctly handled by the lexer, added comment about the current problem with property @MarkingsStruct which seems to disappear after call to SCI_SETILEXER or CreateLexer - corrected usage of ObjC lexer - removed unnecessary filter stuff - use own sections for scintilla and lexilla build targets and allow parallel builds - as libscilex is no longer existing, changed to libscintilla - adapt makefiles and cmake - use VS2019 - started simple changes for createlexer adaptations, nullpointercheck missing on return of lexer name from deprecated LexerNameFromID -> undefined behaviour - movement from id -> lexer name, mostly done via LexerNameFromID + switching off corresponding compiler warning - changed to SCI_SETILEXER from SCI_SETLEXER, SCI_SETLEXERLANGUAGE needs to be corrected, see Scintilla5Migration.html - just commented out: SCI_LOADLEXERLIBRARY Fix #10504, close #11419
		
			
				
	
	
		
			376 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			376 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Scintilla source code edit control
 | |
| /** @file LexTCL.cxx
 | |
|  ** Lexer for TCL language.
 | |
|  **/
 | |
| // Copyright 1998-2001 by Andre Arpin <arpin@kingston.net>
 | |
| // The License.txt file describes the conditions under which this software may be distributed.
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <stdio.h>
 | |
| #include <stdarg.h>
 | |
| #include <assert.h>
 | |
| #include <ctype.h>
 | |
| 
 | |
| #include <string>
 | |
| #include <string_view>
 | |
| 
 | |
| #include "ILexer.h"
 | |
| #include "Scintilla.h"
 | |
| #include "SciLexer.h"
 | |
| 
 | |
| #include "WordList.h"
 | |
| #include "LexAccessor.h"
 | |
| #include "Accessor.h"
 | |
| #include "StyleContext.h"
 | |
| #include "CharacterSet.h"
 | |
| #include "LexerModule.h"
 | |
| 
 | |
| using namespace Lexilla;
 | |
| 
 | |
| // Extended to accept accented characters
 | |
| static inline bool IsAWordChar(int ch) {
 | |
| 	return ch >= 0x80 ||
 | |
| 	       (isalnum(ch) || ch == '_' || ch ==':' || ch=='.'); // : name space separator
 | |
| }
 | |
| 
 | |
| static inline bool IsAWordStart(int ch) {
 | |
| 	return ch >= 0x80 || (ch ==':' || isalpha(ch) || ch == '_');
 | |
| }
 | |
| 
 | |
| static inline bool IsANumberChar(int ch) {
 | |
| 	// Not exactly following number definition (several dots are seen as OK, etc.)
 | |
| 	// but probably enough in most cases.
 | |
| 	return (ch < 0x80) &&
 | |
| 	       (IsADigit(ch, 0x10) || toupper(ch) == 'E' ||
 | |
| 	        ch == '.' || ch == '-' || ch == '+');
 | |
| }
 | |
| 
 | |
| static void ColouriseTCLDoc(Sci_PositionU startPos, Sci_Position length, int , WordList *keywordlists[], Accessor &styler) {
 | |
| #define  isComment(s) (s==SCE_TCL_COMMENT || s==SCE_TCL_COMMENTLINE || s==SCE_TCL_COMMENT_BOX || s==SCE_TCL_BLOCK_COMMENT)
 | |
| 	const bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
 | |
| 	const bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
 | |
| 	bool commentLevel = false;
 | |
| 	bool subBrace = false; // substitution begin with a brace ${.....}
 | |
| 	enum tLineState {LS_DEFAULT, LS_OPEN_COMMENT, LS_OPEN_DOUBLE_QUOTE, LS_COMMENT_BOX, LS_MASK_STATE = 0xf,
 | |
| 	                 LS_COMMAND_EXPECTED = 16, LS_BRACE_ONLY = 32
 | |
| 	                } lineState = LS_DEFAULT;
 | |
| 	bool prevSlash = false;
 | |
| 	int currentLevel = 0;
 | |
| 	bool expected = 0;
 | |
| 	bool subParen = 0;
 | |
| 
 | |
| 	Sci_Position currentLine = styler.GetLine(startPos);
 | |
| 	if (currentLine > 0)
 | |
| 		currentLine--;
 | |
| 	length += startPos - styler.LineStart(currentLine);
 | |
| 	// make sure lines overlap
 | |
| 	startPos = styler.LineStart(currentLine);
 | |
| 
 | |
| 	WordList &keywords = *keywordlists[0];
 | |
| 	WordList &keywords2 = *keywordlists[1];
 | |
| 	WordList &keywords3 = *keywordlists[2];
 | |
| 	WordList &keywords4 = *keywordlists[3];
 | |
| 	WordList &keywords5 = *keywordlists[4];
 | |
| 	WordList &keywords6 = *keywordlists[5];
 | |
| 	WordList &keywords7 = *keywordlists[6];
 | |
| 	WordList &keywords8 = *keywordlists[7];
 | |
| 	WordList &keywords9 = *keywordlists[8];
 | |
| 
 | |
| 	if (currentLine > 0) {
 | |
| 		int ls = styler.GetLineState(currentLine - 1);
 | |
| 		lineState = tLineState(ls & LS_MASK_STATE);
 | |
| 		expected = LS_COMMAND_EXPECTED == tLineState(ls & LS_COMMAND_EXPECTED);
 | |
| 		subBrace = LS_BRACE_ONLY == tLineState(ls & LS_BRACE_ONLY);
 | |
| 		currentLevel = styler.LevelAt(currentLine - 1) >> 17;
 | |
| 		commentLevel = (styler.LevelAt(currentLine - 1) >> 16) & 1;
 | |
| 	} else
 | |
| 		styler.SetLevel(0, SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG);
 | |
| 	bool visibleChars = false;
 | |
| 
 | |
| 	int previousLevel = currentLevel;
 | |
| 	StyleContext sc(startPos, length, SCE_TCL_DEFAULT, styler);
 | |
| 	for (; ; sc.Forward()) {
 | |
| next:
 | |
| 		if (sc.ch=='\r' && sc.chNext == '\n') // only ignore \r on PC process on the mac
 | |
| 			continue;
 | |
| 		bool atEnd = !sc.More();  // make sure we coloured the last word
 | |
| 		if (lineState != LS_DEFAULT) {
 | |
| 			sc.SetState(SCE_TCL_DEFAULT);
 | |
| 			if (lineState == LS_OPEN_COMMENT)
 | |
| 				sc.SetState(SCE_TCL_COMMENTLINE);
 | |
| 			else if (lineState == LS_OPEN_DOUBLE_QUOTE)
 | |
| 				sc.SetState(SCE_TCL_IN_QUOTE);
 | |
| 			else if (lineState == LS_COMMENT_BOX && (sc.ch == '#' || (sc.ch == ' ' && sc.chNext=='#')))
 | |
| 				sc.SetState(SCE_TCL_COMMENT_BOX);
 | |
| 			lineState = LS_DEFAULT;
 | |
| 		}
 | |
| 		if (subBrace) { // ${ overrides every thing even \ except }
 | |
| 			if (sc.ch == '}') {
 | |
| 				subBrace = false;
 | |
| 				sc.SetState(SCE_TCL_OPERATOR);
 | |
| 				sc.ForwardSetState(SCE_TCL_DEFAULT);
 | |
| 				goto next;
 | |
| 			} else
 | |
| 				sc.SetState(SCE_TCL_SUB_BRACE);
 | |
| 			if (!sc.atLineEnd)
 | |
| 				continue;
 | |
| 		} else if (sc.state == SCE_TCL_DEFAULT || sc.state ==SCE_TCL_OPERATOR) {
 | |
| 			expected &= isspacechar(static_cast<unsigned char>(sc.ch)) || IsAWordStart(sc.ch) || sc.ch =='#';
 | |
| 		} else if (sc.state == SCE_TCL_SUBSTITUTION) {
 | |
| 			switch (sc.ch) {
 | |
| 			case '(':
 | |
| 				subParen=true;
 | |
| 				sc.SetState(SCE_TCL_OPERATOR);
 | |
| 				sc.ForwardSetState(SCE_TCL_SUBSTITUTION);
 | |
| 				continue;
 | |
| 			case ')':
 | |
| 				sc.SetState(SCE_TCL_OPERATOR);
 | |
| 				subParen=false;
 | |
| 				continue;
 | |
| 			case '$':
 | |
| 				continue;
 | |
| 			case ',':
 | |
| 				sc.SetState(SCE_TCL_OPERATOR);
 | |
| 				if (subParen) {
 | |
| 					sc.ForwardSetState(SCE_TCL_SUBSTITUTION);
 | |
| 					goto next;	// Already forwarded so avoid loop's Forward()
 | |
| 				}
 | |
| 				continue;
 | |
| 			default :
 | |
| 				// maybe spaces should be allowed ???
 | |
| 				if (!IsAWordChar(sc.ch)) { // probably the code is wrong
 | |
| 					sc.SetState(SCE_TCL_DEFAULT);
 | |
| 					subParen = 0;
 | |
| 				}
 | |
| 				break;
 | |
| 			}
 | |
| 		} else if (isComment(sc.state)) {
 | |
| 		} else if (!IsAWordChar(sc.ch)) {
 | |
| 			if ((sc.state == SCE_TCL_IDENTIFIER && expected) ||  sc.state == SCE_TCL_MODIFIER) {
 | |
| 				char w[100];
 | |
| 				sc.GetCurrent(w, sizeof(w));
 | |
| 				char *s=w;
 | |
| 				if (w[strlen(w)-1]=='\r')
 | |
| 					w[strlen(w)-1]=0;
 | |
| 				while (*s == ':') // ignore leading : like in ::set a 10
 | |
| 					++s;
 | |
| 				bool quote = sc.state == SCE_TCL_IN_QUOTE;
 | |
| 				if (commentLevel  || expected) {
 | |
| 					if (keywords.InList(s)) {
 | |
| 						sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD);
 | |
| 					} else if (keywords2.InList(s)) {
 | |
| 						sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD2);
 | |
| 					} else if (keywords3.InList(s)) {
 | |
| 						sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD3);
 | |
| 					} else if (keywords4.InList(s)) {
 | |
| 						sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD4);
 | |
| 					} else if (sc.GetRelative(-static_cast<Sci_Position>(strlen(s))-1) == '{' &&
 | |
| 					           keywords5.InList(s) && sc.ch == '}') { // {keyword} exactly no spaces
 | |
| 						sc.ChangeState(SCE_TCL_EXPAND);
 | |
| 					}
 | |
| 					if (keywords6.InList(s)) {
 | |
| 						sc.ChangeState(SCE_TCL_WORD5);
 | |
| 					} else if (keywords7.InList(s)) {
 | |
| 						sc.ChangeState(SCE_TCL_WORD6);
 | |
| 					} else if (keywords8.InList(s)) {
 | |
| 						sc.ChangeState(SCE_TCL_WORD7);
 | |
| 					} else if (keywords9.InList(s)) {
 | |
| 						sc.ChangeState(SCE_TCL_WORD8);
 | |
| 					}
 | |
| 				}
 | |
| 				expected = false;
 | |
| 				sc.SetState(quote ? SCE_TCL_IN_QUOTE : SCE_TCL_DEFAULT);
 | |
| 			} else if (sc.state == SCE_TCL_MODIFIER || sc.state == SCE_TCL_IDENTIFIER) {
 | |
| 				sc.SetState(SCE_TCL_DEFAULT);
 | |
| 			}
 | |
| 		}
 | |
| 		if (atEnd)
 | |
| 			break;
 | |
| 		if (sc.atLineEnd) {
 | |
| 			lineState = LS_DEFAULT;
 | |
| 			currentLine = styler.GetLine(sc.currentPos);
 | |
| 			if (foldComment && sc.state!=SCE_TCL_COMMENT && isComment(sc.state)) {
 | |
| 				if (currentLevel == 0) {
 | |
| 					++currentLevel;
 | |
| 					commentLevel = true;
 | |
| 				}
 | |
| 			} else {
 | |
| 				if (visibleChars && commentLevel) {
 | |
| 					--currentLevel;
 | |
| 					--previousLevel;
 | |
| 					commentLevel = false;
 | |
| 				}
 | |
| 			}
 | |
| 			int flag = 0;
 | |
| 			if (!visibleChars && foldCompact)
 | |
| 				flag = SC_FOLDLEVELWHITEFLAG;
 | |
| 			if (currentLevel > previousLevel)
 | |
| 				flag = SC_FOLDLEVELHEADERFLAG;
 | |
| 			styler.SetLevel(currentLine, flag + previousLevel + SC_FOLDLEVELBASE + (currentLevel << 17) + (commentLevel << 16));
 | |
| 
 | |
| 			// Update the line state, so it can be seen by next line
 | |
| 			if (sc.state == SCE_TCL_IN_QUOTE) {
 | |
| 				lineState = LS_OPEN_DOUBLE_QUOTE;
 | |
| 			} else {
 | |
| 				if (prevSlash) {
 | |
| 					if (isComment(sc.state))
 | |
| 						lineState = LS_OPEN_COMMENT;
 | |
| 				} else if (sc.state == SCE_TCL_COMMENT_BOX)
 | |
| 					lineState = LS_COMMENT_BOX;
 | |
| 			}
 | |
| 			styler.SetLineState(currentLine,
 | |
| 			                    (subBrace ? LS_BRACE_ONLY : 0) |
 | |
| 			                    (expected ? LS_COMMAND_EXPECTED : 0)  | lineState);
 | |
| 			if (lineState == LS_COMMENT_BOX)
 | |
| 				sc.ForwardSetState(SCE_TCL_COMMENT_BOX);
 | |
| 			else if (lineState == LS_OPEN_DOUBLE_QUOTE)
 | |
| 				sc.ForwardSetState(SCE_TCL_IN_QUOTE);
 | |
| 			else
 | |
| 				sc.ForwardSetState(SCE_TCL_DEFAULT);
 | |
| 			prevSlash = false;
 | |
| 			previousLevel = currentLevel;
 | |
| 			visibleChars = false;
 | |
| 			goto next;
 | |
| 		}
 | |
| 
 | |
| 		if (prevSlash) {
 | |
| 			prevSlash = false;
 | |
| 			if (sc.ch == '#' && IsANumberChar(sc.chNext))
 | |
| 				sc.ForwardSetState(SCE_TCL_NUMBER);
 | |
| 			continue;
 | |
| 		}
 | |
| 		prevSlash = sc.ch == '\\';
 | |
| 		if (isComment(sc.state))
 | |
| 			continue;
 | |
| 		if (sc.atLineStart) {
 | |
| 			visibleChars = false;
 | |
| 			if (sc.state!=SCE_TCL_IN_QUOTE && !isComment(sc.state))
 | |
| 			{
 | |
| 				sc.SetState(SCE_TCL_DEFAULT);
 | |
| 				expected = IsAWordStart(sc.ch)|| isspacechar(static_cast<unsigned char>(sc.ch));
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		switch (sc.state) {
 | |
| 		case SCE_TCL_NUMBER:
 | |
| 			if (!IsANumberChar(sc.ch))
 | |
| 				sc.SetState(SCE_TCL_DEFAULT);
 | |
| 			break;
 | |
| 		case SCE_TCL_IN_QUOTE:
 | |
| 			if (sc.ch == '"') {
 | |
| 				sc.ForwardSetState(SCE_TCL_DEFAULT);
 | |
| 				visibleChars = true; // necessary if a " is the first and only character on a line
 | |
| 				goto next;
 | |
| 			} else if (sc.ch == '[' || sc.ch == ']' || sc.ch == '$') {
 | |
| 				sc.SetState(SCE_TCL_OPERATOR);
 | |
| 				expected = sc.ch == '[';
 | |
| 				sc.ForwardSetState(SCE_TCL_IN_QUOTE);
 | |
| 				goto next;
 | |
| 			}
 | |
| 			continue;
 | |
| 		case SCE_TCL_OPERATOR:
 | |
| 			sc.SetState(SCE_TCL_DEFAULT);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if (sc.ch == '#') {
 | |
| 			if (visibleChars) {
 | |
| 				if (sc.state != SCE_TCL_IN_QUOTE && expected)
 | |
| 					sc.SetState(SCE_TCL_COMMENT);
 | |
| 			} else {
 | |
| 				sc.SetState(SCE_TCL_COMMENTLINE);
 | |
| 				if (sc.chNext == '~')
 | |
| 					sc.SetState(SCE_TCL_BLOCK_COMMENT);
 | |
| 				if (sc.atLineStart && (sc.chNext == '#' || sc.chNext == '-'))
 | |
| 					sc.SetState(SCE_TCL_COMMENT_BOX);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (!isspacechar(static_cast<unsigned char>(sc.ch))) {
 | |
| 			visibleChars = true;
 | |
| 		}
 | |
| 
 | |
| 		if (sc.ch == '\\') {
 | |
| 			prevSlash = true;
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		// Determine if a new state should be entered.
 | |
| 		if (sc.state == SCE_TCL_DEFAULT) {
 | |
| 			if (IsAWordStart(sc.ch)) {
 | |
| 				sc.SetState(SCE_TCL_IDENTIFIER);
 | |
| 			} else if (IsADigit(sc.ch) && !IsAWordChar(sc.chPrev)) {
 | |
| 				sc.SetState(SCE_TCL_NUMBER);
 | |
| 			} else {
 | |
| 				switch (sc.ch) {
 | |
| 				case '\"':
 | |
| 					sc.SetState(SCE_TCL_IN_QUOTE);
 | |
| 					break;
 | |
| 				case '{':
 | |
| 					sc.SetState(SCE_TCL_OPERATOR);
 | |
| 					expected = true;
 | |
| 					++currentLevel;
 | |
| 					break;
 | |
| 				case '}':
 | |
| 					sc.SetState(SCE_TCL_OPERATOR);
 | |
| 					expected = true;
 | |
| 					--currentLevel;
 | |
| 					break;
 | |
| 				case '[':
 | |
| 					expected = true;
 | |
| 					[[fallthrough]];
 | |
| 				case ']':
 | |
| 				case '(':
 | |
| 				case ')':
 | |
| 					sc.SetState(SCE_TCL_OPERATOR);
 | |
| 					break;
 | |
| 				case ';':
 | |
| 					expected = true;
 | |
| 					break;
 | |
| 				case '$':
 | |
| 					subParen = 0;
 | |
| 					if (sc.chNext != '{') {
 | |
| 						sc.SetState(SCE_TCL_SUBSTITUTION);
 | |
| 					} else {
 | |
| 						sc.SetState(SCE_TCL_OPERATOR);  // $
 | |
| 						sc.Forward();  // {
 | |
| 						sc.ForwardSetState(SCE_TCL_SUB_BRACE);
 | |
| 						subBrace = true;
 | |
| 					}
 | |
| 					break;
 | |
| 				case '#':
 | |
| 					if ((isspacechar(static_cast<unsigned char>(sc.chPrev))||
 | |
| 					        isoperator(static_cast<char>(sc.chPrev))) && IsADigit(sc.chNext,0x10))
 | |
| 						sc.SetState(SCE_TCL_NUMBER);
 | |
| 					break;
 | |
| 				case '-':
 | |
| 					sc.SetState(IsADigit(sc.chNext)? SCE_TCL_NUMBER: SCE_TCL_MODIFIER);
 | |
| 					break;
 | |
| 				default:
 | |
| 					if (isoperator(static_cast<char>(sc.ch))) {
 | |
| 						sc.SetState(SCE_TCL_OPERATOR);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	sc.Complete();
 | |
| }
 | |
| 
 | |
| static const char *const tclWordListDesc[] = {
 | |
| 	"TCL Keywords",
 | |
| 	"TK Keywords",
 | |
| 	"iTCL Keywords",
 | |
| 	"tkCommands",
 | |
| 	"expand",
 | |
| 	"user1",
 | |
| 	"user2",
 | |
| 	"user3",
 | |
| 	"user4",
 | |
| 	0
 | |
| };
 | |
| 
 | |
| // this code supports folding in the colourizer
 | |
| LexerModule lmTCL(SCLEX_TCL, ColouriseTCLDoc, "tcl", 0, tclWordListDesc);
 |