diff options
Diffstat (limited to 'lexers')
| -rw-r--r-- | lexers/LexBash.cxx | 265 | 
1 files changed, 221 insertions, 44 deletions
| diff --git a/lexers/LexBash.cxx b/lexers/LexBash.cxx index 5bbd563d5..1571e1976 100644 --- a/lexers/LexBash.cxx +++ b/lexers/LexBash.cxx @@ -12,16 +12,23 @@  #include <stdarg.h>  #include <assert.h> +#include <string> +#include <vector> +#include <map> +  #include "ILexer.h"  #include "Scintilla.h"  #include "SciLexer.h" +#include "StringCopy.h"  #include "WordList.h"  #include "LexAccessor.h" -#include "Accessor.h"  #include "StyleContext.h"  #include "CharacterSet.h"  #include "LexerModule.h" +#include "OptionSet.h" +#include "SubStyles.h" +#include "DefaultLexer.h"  using namespace Scintilla; @@ -58,7 +65,9 @@ using namespace Scintilla;  #define BASH_DELIM_STACK_MAX	7 -static inline int translateBashDigit(int ch) { +namespace { + +inline int translateBashDigit(int ch) {  	if (ch >= '0' && ch <= '9') {  		return ch - '0';  	} else if (ch >= 'a' && ch <= 'z') { @@ -73,7 +82,7 @@ static inline int translateBashDigit(int ch) {  	return BASH_BASE_ERROR;  } -static inline int getBashNumberBase(char *s) { +inline int getBashNumberBase(char *s) {  	int i = 0;  	int base = 0;  	while (*s) { @@ -86,7 +95,7 @@ static inline int getBashNumberBase(char *s) {  	return base;  } -static int opposite(int ch) { +int opposite(int ch) {  	if (ch == '(') return ')';  	if (ch == '[') return ']';  	if (ch == '{') return '}'; @@ -94,7 +103,7 @@ static int opposite(int ch) {  	return ch;  } -static int GlobScan(StyleContext &sc) { +int GlobScan(StyleContext &sc) {  	// forward scan for zsh globs, disambiguate versus bash arrays  	// complex expressions may still fail, e.g. unbalanced () '' "" etc  	int c, sLen = 0; @@ -120,10 +129,172 @@ static int GlobScan(StyleContext &sc) {  	return 0;  } -static void ColouriseBashDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, -							 WordList *keywordlists[], Accessor &styler) { +bool IsCommentLine(Sci_Position line, LexAccessor &styler) { +	Sci_Position pos = styler.LineStart(line); +	Sci_Position eol_pos = styler.LineStart(line + 1) - 1; +	for (Sci_Position i = pos; i < eol_pos; i++) { +		char ch = styler[i]; +		if (ch == '#') +			return true; +		else if (ch != ' ' && ch != '\t') +			return false; +	} +	return false; +} + +struct OptionsBash { +	bool fold; +	bool foldComment; +	bool foldCompact; + +	OptionsBash() { +		fold = false; +		foldComment = false; +		foldCompact = true; +	} +}; + +const char * const bashWordListDesc[] = { +	"Keywords", +	0 +}; + +struct OptionSetBash : public OptionSet<OptionsBash> { +	OptionSetBash() { +		DefineProperty("fold", &OptionsBash::fold); + +		DefineProperty("fold.comment", &OptionsBash::foldComment, +			"This option enables folding multi-line comments."); + +		DefineProperty("fold.compact", &OptionsBash::foldCompact); + +		DefineWordListSets(bashWordListDesc); +	} +}; + +const char styleSubable[] = { SCE_SH_IDENTIFIER, SCE_SH_SCALAR, 0 }; + +LexicalClass lexicalClasses[] = { +	// Lexer Bash SCLEX_BASH SCE_SH_: +	0, "SCE_SH_DEFAULT", "default", "White space", +	1, "SCE_SH_ERROR", "error", "Error", +	2, "SCE_SH_COMMENTLINE", "comment line", "Line comment: #", +	3, "SCE_SH_NUMBER", "literal numeric", "Number", +	4, "SCE_SH_WORD", "keyword", "Keyword", +	5, "SCE_SH_STRING", "literal string", "String", +	6, "SCE_SH_CHARACTER", "literal string", "Single quoted string", +	7, "SCE_SH_OPERATOR", "operator", "Operators", +	8, "SCE_SH_IDENTIFIER", "identifier", "Identifiers", +	9, "SCE_SH_SCALAR", "identifier", "Scalar variable", +	10, "SCE_SH_PARAM", "identifier", "Parameter", +	11, "SCE_SH_BACKTICKS", "literal string", "Backtick quoted command", +	12, "SCE_SH_HERE_DELIM", "operator", "Heredoc delimiter", +	13, "SCE_SH_HERE_Q", "literal string", "Heredoc quoted string", +}; + +} + +class LexerBash : public DefaultLexer { +	WordList keywords; +	OptionsBash options; +	OptionSetBash osBash; +	enum { ssIdentifier, ssScalar }; +	SubStyles subStyles; +public: +	LexerBash() : +		DefaultLexer(lexicalClasses, ELEMENTS(lexicalClasses)), +		subStyles(styleSubable, 0x80, 0x40, 0) { +	} +	virtual ~LexerBash() { +	} +	void SCI_METHOD Release() override { +		delete this; +	} +	int SCI_METHOD Version() const override { +		return lvMetaData; +	} +	const char * SCI_METHOD PropertyNames() override { +		return osBash.PropertyNames(); +	} +	int SCI_METHOD PropertyType(const char* name) override { +		return osBash.PropertyType(name); +	} +	const char * SCI_METHOD DescribeProperty(const char *name) override { +		return osBash.DescribeProperty(name); +	} +	Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override; +	const char * SCI_METHOD DescribeWordListSets() override { +		return osBash.DescribeWordListSets(); +	} +	Sci_Position SCI_METHOD WordListSet(int n, const char *wl) override; +	void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override; +	void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override; + +	void * SCI_METHOD PrivateCall(int, void *) override { +		return 0; +	} + +	int SCI_METHOD AllocateSubStyles(int styleBase, int numberStyles) override { +		return subStyles.Allocate(styleBase, numberStyles); +	} +	int SCI_METHOD SubStylesStart(int styleBase) override { +		return subStyles.Start(styleBase); +	} +	int SCI_METHOD SubStylesLength(int styleBase) override { +		return subStyles.Length(styleBase); +	} +	int SCI_METHOD StyleFromSubStyle(int subStyle) override { +		const int styleBase = subStyles.BaseStyle(subStyle); +		return styleBase; +	} +	int SCI_METHOD PrimaryStyleFromStyle(int style) override { +		return style; +	} +	void SCI_METHOD FreeSubStyles() override { +		subStyles.Free(); +	} +	void SCI_METHOD SetIdentifiers(int style, const char *identifiers) override { +		subStyles.SetIdentifiers(style, identifiers); +	} +	int SCI_METHOD DistanceToSecondaryStyles() override { +		return 0; +	} +	const char *SCI_METHOD GetSubStyleBases() override { +		return styleSubable; +	} + +	static ILexer *LexerFactoryBash() { +		return new LexerBash(); +	} +}; + +Sci_Position SCI_METHOD LexerBash::PropertySet(const char *key, const char *val) { +	if (osBash.PropertySet(&options, key, val)) { +		return 0; +	} +	return -1; +} + +Sci_Position SCI_METHOD LexerBash::WordListSet(int n, const char *wl) { +	WordList *wordListN = 0; +	switch (n) { +	case 0: +		wordListN = &keywords; +		break; +	} +	Sci_Position firstModification = -1; +	if (wordListN) { +		WordList wlNew; +		wlNew.Set(wl); +		if (*wordListN != wlNew) { +			wordListN->Set(wl); +			firstModification = 0; +		} +	} +	return firstModification; +} -	WordList &keywords = *keywordlists[0]; +void SCI_METHOD LexerBash::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {  	WordList cmdDelimiter, bashStruct, bashStruct_in;  	cmdDelimiter.Set("| || |& & && ; ;; ( ) { }");  	bashStruct.Set("if elif fi while until else then do done esac eval"); @@ -237,11 +408,15 @@ static void ColouriseBashDoc(Sci_PositionU startPos, Sci_Position length, int in  	};  	QuoteStackCls QuoteStack; +	const WordClassifier &classifierIdentifiers = subStyles.Classifier(SCE_SH_IDENTIFIER); +	const WordClassifier &classifierScalars = subStyles.Classifier(SCE_SH_SCALAR); +  	int numBase = 0;  	int digit;  	Sci_PositionU endPos = startPos + length;  	int cmdState = BASH_CMD_START;  	int testExprType = 0; +	LexAccessor styler(pAccess);  	// Always backtracks to the start of a line that is not a continuation  	// of the previous line (i.e. start of a bash command segment) @@ -306,6 +481,11 @@ static void ColouriseBashDoc(Sci_PositionU startPos, Sci_Position length, int in  					char s[500];  					char s2[10];  					sc.GetCurrent(s, sizeof(s)); +					int identifierStyle = SCE_SH_IDENTIFIER; +					int subStyle = classifierIdentifiers.ValueFor(s); +					if (subStyle >= 0) { +						identifierStyle = subStyle; +					}  					// allow keywords ending in a whitespace or command delimiter  					s2[0] = static_cast<char>(sc.ch);  					s2[1] = '\0'; @@ -317,7 +497,7 @@ static void ColouriseBashDoc(Sci_PositionU startPos, Sci_Position length, int in  						else if (strcmp(s, "do") == 0 && keywordEnds)  							cmdStateNew = BASH_CMD_START;  						else -							sc.ChangeState(SCE_SH_IDENTIFIER); +							sc.ChangeState(identifierStyle);  						sc.SetState(SCE_SH_DEFAULT);  						break;  					} @@ -327,42 +507,49 @@ static void ColouriseBashDoc(Sci_PositionU startPos, Sci_Position length, int in  							cmdStateNew = BASH_CMD_TEST;  							testExprType = 0;  						} else -							sc.ChangeState(SCE_SH_IDENTIFIER); +							sc.ChangeState(identifierStyle);  					}  					// detect bash construct keywords  					else if (bashStruct.InList(s)) {  						if (cmdState == BASH_CMD_START && keywordEnds)  							cmdStateNew = BASH_CMD_START;  						else -							sc.ChangeState(SCE_SH_IDENTIFIER); +							sc.ChangeState(identifierStyle);  					}  					// 'for'|'case'|'select' needs 'in'|'do' to be highlighted later  					else if (bashStruct_in.InList(s)) {  						if (cmdState == BASH_CMD_START && keywordEnds)  							cmdStateNew = BASH_CMD_WORD;  						else -							sc.ChangeState(SCE_SH_IDENTIFIER); +							sc.ChangeState(identifierStyle);  					}  					// disambiguate option items and file test operators  					else if (s[0] == '-') {  						if (cmdState != BASH_CMD_TEST) -							sc.ChangeState(SCE_SH_IDENTIFIER); +							sc.ChangeState(identifierStyle);  					}  					// disambiguate keywords and identifiers  					else if (cmdState != BASH_CMD_START  						  || !(keywords.InList(s) && keywordEnds)) { -						sc.ChangeState(SCE_SH_IDENTIFIER); +						sc.ChangeState(identifierStyle);  					}  					sc.SetState(SCE_SH_DEFAULT);  				}  				break;  			case SCE_SH_IDENTIFIER: -				if (sc.chPrev == '\\') {	// for escaped chars -					sc.ForwardSetState(SCE_SH_DEFAULT); -				} else if (!setWord.Contains(sc.ch)) { -					sc.SetState(SCE_SH_DEFAULT); -				} else if (cmdState == BASH_CMD_ARITH && !setWordStart.Contains(sc.ch)) { -					sc.SetState(SCE_SH_DEFAULT); +				if (sc.chPrev == '\\' || !setWord.Contains(sc.ch) || +					  (cmdState == BASH_CMD_ARITH && !setWordStart.Contains(sc.ch))) { +					char s[500]; +					sc.GetCurrent(s, sizeof(s)); +					int subStyle = classifierIdentifiers.ValueFor(s); +					if (subStyle >= 0) { +						sc.ChangeState(subStyle); +					} +					if (sc.chPrev == '\\') {	// for escaped chars +						sc.ForwardSetState(SCE_SH_DEFAULT); +					} else { +						sc.SetState(SCE_SH_DEFAULT); +					}  				}  				break;  			case SCE_SH_NUMBER: @@ -516,6 +703,12 @@ static void ColouriseBashDoc(Sci_PositionU startPos, Sci_Position length, int in  				break;  			case SCE_SH_SCALAR:	// variable names  				if (!setParam.Contains(sc.ch)) { +					char s[500]; +					sc.GetCurrent(s, sizeof(s)); +					int subStyle = classifierScalars.ValueFor(&s[1]); // skip the $ +					if (subStyle >= 0) { +						sc.ChangeState(subStyle); +					}  					if (sc.LengthCurrent() == 1) {  						// Special variable: $(, $_ etc.  						sc.ForwardSetState(SCE_SH_DEFAULT); @@ -799,23 +992,12 @@ static void ColouriseBashDoc(Sci_PositionU startPos, Sci_Position length, int in  	sc.Complete();  } -static bool IsCommentLine(Sci_Position line, Accessor &styler) { -	Sci_Position pos = styler.LineStart(line); -	Sci_Position eol_pos = styler.LineStart(line + 1) - 1; -	for (Sci_Position i = pos; i < eol_pos; i++) { -		char ch = styler[i]; -		if (ch == '#') -			return true; -		else if (ch != ' ' && ch != '\t') -			return false; -	} -	return false; -} +void SCI_METHOD LexerBash::Fold(Sci_PositionU startPos, Sci_Position length, int, IDocument *pAccess) { +	if(!options.fold) +		return; + +	LexAccessor styler(pAccess); -static void FoldBashDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], -						Accessor &styler) { -	bool foldComment = styler.GetPropertyInt("fold.comment") != 0; -	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;  	Sci_PositionU endPos = startPos + length;  	int visibleChars = 0;  	int skipHereCh = 0; @@ -833,7 +1015,7 @@ static void FoldBashDoc(Sci_PositionU startPos, Sci_Position length, int, WordLi  		styleNext = styler.StyleAt(i + 1);  		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');  		// Comment folding -		if (foldComment && atEOL && IsCommentLine(lineCurrent, styler)) +		if (options.foldComment && atEOL && IsCommentLine(lineCurrent, styler))  		{  			if (!IsCommentLine(lineCurrent - 1, styler)  				&& IsCommentLine(lineCurrent + 1, styler)) @@ -880,7 +1062,7 @@ static void FoldBashDoc(Sci_PositionU startPos, Sci_Position length, int, WordLi  		}  		if (atEOL) {  			int lev = levelPrev; -			if (visibleChars == 0 && foldCompact) +			if (visibleChars == 0 && options.foldCompact)  				lev |= SC_FOLDLEVELWHITEFLAG;  			if ((levelCurrent > levelPrev) && (visibleChars > 0))  				lev |= SC_FOLDLEVELHEADERFLAG; @@ -899,9 +1081,4 @@ static void FoldBashDoc(Sci_PositionU startPos, Sci_Position length, int, WordLi  	styler.SetLevel(lineCurrent, levelPrev | flagsNext);  } -static const char * const bashWordListDesc[] = { -	"Keywords", -	0 -}; - -LexerModule lmBash(SCLEX_BASH, ColouriseBashDoc, "bash", FoldBashDoc, bashWordListDesc); +LexerModule lmBash(SCLEX_BASH, LexerBash::LexerFactoryBash, "bash", bashWordListDesc); | 
