From e44c28561697e4d8c3e71b99d38c07903f2b5f8b Mon Sep 17 00:00:00 2001 From: zufuliu Date: Mon, 21 Jun 2021 23:53:32 +0800 Subject: [PATCH] Refactoring Batch lexer, issue #332. --- build/make_zip.bat | 4 +- scintilla/include/SciLexer.h | 1 + scintilla/include/SciLexer.iface | 1 + scintilla/lexers/LexBatch.cxx | 318 ++++++++++++++++++------------- src/EditLexers/stlBatch.c | 24 +-- src/Styles.c | 8 +- tools/KeywordCore.py | 4 +- tools/lang/Batch.bat | 53 +++--- 8 files changed, 235 insertions(+), 178 deletions(-) diff --git a/build/make_zip.bat b/build/make_zip.bat index c4ba9560f7..664ec1f0d0 100644 --- a/build/make_zip.bat +++ b/build/make_zip.bat @@ -112,7 +112,7 @@ SET "EXIT_ON_ERROR=%~1" CALL :SubGetVersion CALL :SubDetectSevenzipPath -IF /I "%SEVENZIP%" == "" CALL :SUBMSG "ERROR" "7za wasn't found!" +IF NOT EXIST "%SEVENZIP%" CALL :SUBMSG "ERROR" "7za wasn't found!" IF /I "%COMPILER%" == "GCC" ( SET INPUTDIR_AVX2=bin\%CONFIG%_AVX2 @@ -222,7 +222,7 @@ IF EXIST "%SEVENZIP_PATH%" (SET "SEVENZIP=%SEVENZIP_PATH%" & EXIT /B) FOR /F "tokens=2*" %%A IN ( 'REG QUERY "HKLM\SOFTWARE\7-Zip" /v "Path" 2^>NUL ^| FIND "REG_SZ" ^|^| REG QUERY "HKLM\SOFTWARE\Wow6432Node\7-Zip" /v "Path" 2^>NUL ^| FIND "REG_SZ"') DO SET "SEVENZIP=%%B\7z.exe" -EXIT /B +IF EXIST "%SEVENZIP%" EXIT /B FOR /F "tokens=2*" %%A IN ( 'REG QUERY "HKLM\SOFTWARE\7-Zip-Zstandard" /v "Path" 2^>NUL ^| FIND "REG_SZ" ^|^| diff --git a/scintilla/include/SciLexer.h b/scintilla/include/SciLexer.h index f524030576..798caea3d3 100644 --- a/scintilla/include/SciLexer.h +++ b/scintilla/include/SciLexer.h @@ -474,6 +474,7 @@ #define SCE_BAT_STRINGSQ 12 #define SCE_BAT_STRINGBT 13 #define SCE_BAT_NOT_BATCH 14 +#define SCE_BAT_LINE_CONTINUATION 15 #define SCE_MAKE_DEFAULT 0 #define SCE_MAKE_COMMENT 1 #define SCE_MAKE_PREPROCESSOR 2 diff --git a/scintilla/include/SciLexer.iface b/scintilla/include/SciLexer.iface index e2ac159ff6..afe319be57 100644 --- a/scintilla/include/SciLexer.iface +++ b/scintilla/include/SciLexer.iface @@ -689,6 +689,7 @@ val SCE_BAT_STRINGDQ=11 val SCE_BAT_STRINGSQ=12 val SCE_BAT_STRINGBT=13 val SCE_BAT_NOT_BATCH=14 +val SCE_BAT_LINE_CONTINUATION=15 # Lexical states for SCLEX_TCMD #lex TCMD=SCLEX_TCMD SCE_TCMD_ #val SCE_TCMD_DEFAULT=0 diff --git a/scintilla/lexers/LexBatch.cxx b/scintilla/lexers/LexBatch.cxx index 39145b79b2..7edc33ba3f 100644 --- a/scintilla/lexers/LexBatch.cxx +++ b/scintilla/lexers/LexBatch.cxx @@ -27,23 +27,31 @@ namespace { enum { BatchLineStateMaskEmptyLine = 1 << 0, - BatchLineStateMaskVisibleChars = 1 << 1, - BatchLineStateLineContinuation = 1 << 2, + BatchLineStateLineContinuation = 1 << 1, }; -// / \ : * ? < > " | -constexpr bool IsBatSpec(int ch) noexcept { - return ch == ':' || ch == '?' || ch == '%' || ch == '!' || ch == '\'' || ch == '\"' || ch == '`'; -} -constexpr bool IsBatOp(int ch, bool inEcho) noexcept { - return ch == '&' || ch == '|' || ch == '<' || ch == '>' || ch == '(' || ch == ')' - || (!inEcho && (ch == '=' || ch == '@' || ch == ';' || ch == '*' || ch == ',')); +enum class Command { + None, + Echo, + Argument, + Call, + Goto, + Set, +}; + +constexpr bool IsDrive(int chPrev, int chNext) noexcept { + return (chNext == '\\' || chNext == '/') && IsAlpha(chPrev); } -constexpr bool IsWordStart(int ch) noexcept { - return IsGraphic(ch) && !(IsBatOp(ch, false) || IsBatSpec(ch) || ch == '.'); + +constexpr bool IsBatchOperator(int ch, Command command) noexcept { + return AnyOf(ch, '&', '|', '<', '>', '(', ')') + || (command != Command::Echo && AnyOf(ch, '=', '@', ',', ';', '?', '*')); } -constexpr bool IsWordChar(int ch) noexcept { - return IsGraphic(ch) && !(IsBatOp(ch, false) || IsBatSpec(ch)); + +constexpr bool IsFileNameChar(int ch) noexcept { + // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + return IsGraphic(ch) && !AnyOf(ch, '<', '>', ':', '"', '|', '?', '*', // reserved characters + '&', '(', ')', ',', ';'); } constexpr bool IsLabelStart(int ch) noexcept { @@ -65,18 +73,25 @@ constexpr bool IsVariableEscapeChar(int ch) noexcept { return IsGraphic(ch) && !AnyOf(ch, '%', ',', ';', '='); } -// someone's, don't -constexpr bool IsSingleQuotedString(int ch, int chPrev, int chNext) noexcept { - return ch == '\'' && !((chPrev >= 0x80 || IsAlphaNumeric(chPrev)) && (chNext == 's' || chNext == 't' || chNext == 'S' || chNext == 'T')); +constexpr bool IsStringStyle(int style) noexcept { + return style == SCE_BAT_STRINGDQ || style == SCE_BAT_STRINGSQ || style == SCE_BAT_STRINGBT; } -bool GetBatEscapeLen(StyleContext &sc, int &length) noexcept { +bool DetectBatchEscapeChar(StyleContext &sc, int &outerStyle) noexcept { // Escape Characters https://www.robvanderwoude.com/escapechars.php - length = 0; + int length = 0; + const int state = (sc.state == SCE_BAT_ESCAPECHAR) ? outerStyle : sc.state; + switch (sc.ch) { case '^': if (IsEOLChar(sc.chNext)) { - // line continuation + if (IsStringStyle(state)) { + outerStyle = state; + sc.SetState(SCE_BAT_ESCAPECHAR); + } else { + outerStyle = SCE_BAT_DEFAULT; + sc.SetState(SCE_BAT_LINE_CONTINUATION); + } return true; } if (sc.chNext == '^') { @@ -94,7 +109,7 @@ bool GetBatEscapeLen(StyleContext &sc, int &length) noexcept { case '\"': // Inside the search pattern of FIND - if (sc.chNext == '"' && (sc.state == SCE_BAT_STRINGDQ || sc.state == SCE_BAT_STRINGSQ || sc.state == SCE_BAT_STRINGBT)) { + if (sc.chNext == '"' && IsStringStyle(state)) { length = 1; } break; @@ -104,17 +119,31 @@ bool GetBatEscapeLen(StyleContext &sc, int &length) noexcept { break; } - return length != 0; + if (length != 0) { + outerStyle = IsStringStyle(state) ? state : SCE_BAT_DEFAULT; + sc.SetState(SCE_BAT_ESCAPECHAR); + sc.Forward(length); + return true; + } + return false; } constexpr bool IsTildeExpansion(int ch) noexcept { return AnyOf(ch, 'f', 'd', 'p', 'n', 'x', 's', 'a', 't', 'z'); } -char DetectBatVariable(StyleContext &sc, int &varQuoteChar) { - const int state = sc.state; +bool DetectBatchVariable(StyleContext &sc, int &outerStyle, int &varQuoteChar) { varQuoteChar = '\0'; + if (!IsGraphic(sc.chNext) || sc.chNext == '!' || (sc.ch == '!' && sc.chNext == '%')) { + return false; + } + + outerStyle = IsStringStyle(sc.state) ? sc.state : SCE_BAT_DEFAULT; sc.SetState(SCE_BAT_VARIABLE); + if (sc.ch == '!') { + varQuoteChar = '!'; + return true; + } if (sc.chNext == '*' || IsADigit(sc.chNext)) { // %*, %1 ... %9 sc.Forward(); @@ -146,11 +175,8 @@ char DetectBatVariable(StyleContext &sc, int &varQuoteChar) { // see https://www.robvanderwoude.com/clevertricks.php sc.Forward(); } - } else if (IsGraphic(sc.chNext) && !(sc.chNext == '%' || sc.chNext == '!')) { - varQuoteChar = sc.ch; } else { - sc.ChangeState(state); - return false; + varQuoteChar = '%'; } return true; } @@ -162,12 +188,14 @@ static_assert(DefaultNestedStateBaseStyle + 3 == SCE_BAT_STRINGBT); void ColouriseBatchDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, LexerWordList keywordLists, Accessor &styler) { const bool fold = styler.GetPropertyInt("fold", 1) & true; int varQuoteChar = '\0'; // %var% or !var! after SetLocal EnableDelayedExpansion + int outerStyle = SCE_BAT_DEFAULT; int logicalVisibleChars = 0; int lineVisibleChars = 0; int prevLineState = 0; - bool inEcho = false; - int escapeLen = 0; + Command command = Command::None; int parenCount = 0; + int chPrevNonWhite = 0; + int stylePrevNonWhite = SCE_BAT_DEFAULT; StyleContext sc(startPos, length, initStyle, styler); std::vector nestedState; @@ -177,22 +205,27 @@ void ColouriseBatchDoc(Sci_PositionU startPos, Sci_Position length, int initStyl prevLineState = styler.GetLineState(sc.currentLine - 1); /* 1: empty line - 1: logicalVisibleChars 1: line continuation + 4: command 8: parenCount : nestedState */ parenCount = (prevLineState >> 8) & 0xff; if (prevLineState & BatchLineStateLineContinuation) { - logicalVisibleChars = prevLineState & BatchLineStateMaskVisibleChars; + ++logicalVisibleChars; + command = static_cast((prevLineState >> 4) & 15); } const int lineState = prevLineState >> 16; if (lineState) { UnpackLineState(lineState, nestedState); } } + if (startPos != 0 && initStyle == SCE_BAT_DEFAULT) { + LookbackNonWhite(styler, startPos, SCE_BAT_DEFAULT, chPrevNonWhite, stylePrevNonWhite); + } int levelNext = levelCurrent; + int parenBefore = parenCount; while (sc.More()) { switch (sc.state) { @@ -201,43 +234,46 @@ void ColouriseBatchDoc(Sci_PositionU startPos, Sci_Position length, int initStyl break; case SCE_BAT_IDENTIFIER: - if ((sc.ch == '^' || sc.ch == '%') && GetBatEscapeLen(sc, escapeLen)) { - sc.SetState(SCE_BAT_ESCAPECHAR); - sc.Forward(escapeLen); - } else if ((sc.ch == '%' || sc.ch == '!') && DetectBatVariable(sc, varQuoteChar)) { - // nop - } else if (!IsWordChar(sc.ch)) { - char s[260]; + if (((sc.ch == '^' || sc.ch == '%') && DetectBatchEscapeChar(sc, outerStyle)) + || ((sc.ch == '%' || sc.ch == '!') && DetectBatchVariable(sc, outerStyle, varQuoteChar))) { + command = Command::Argument; + } else if (logicalVisibleChars == 1 && sc.ch == ':' && IsDrive(sc.chPrev, sc.chNext)) { + ++logicalVisibleChars; + sc.Forward(); + } else if (!IsFileNameChar(sc.ch)) { + char s[32]; sc.GetCurrentLowered(s, sizeof(s)); if (StrEqual(s, "rem")) { sc.ChangeState(SCE_BAT_COMMENT); } else { - if (!inEcho && keywordLists[0]->InList(s)) { + if (keywordLists[0]->InList(s)) { + command = Command::None; + parenBefore = parenCount; sc.ChangeState(SCE_BAT_WORD); - inEcho = false; - if (StrEqualsAny(s, "echo", "echo.", "title", "cd", "chdir")) { - // commands with unquoted string argument - inEcho = true; + if (StrEqualsAny(s, "echo", "echo.", "title")) { + command = Command::Echo; } else if (StrEqualsAny(s, "do", "else")) { logicalVisibleChars = 0; - } else { - const bool isGoto = StrEqual(s, "goto"); - if (isGoto || StrEqual(s, "call")) { - sc.SetState(SCE_BAT_DEFAULT); - while (IsASpaceOrTab(sc.ch)) { - sc.Forward(); - } - if (sc.ch == ':' || (isGoto && IsGraphic(sc.ch))) { - sc.SetState(SCE_BAT_LABEL); - } - } + } else if (StrEqual(s, "goto")) { + command = Command::Goto; + } else if (StrEqual(s, "call")) { + command = Command::Call; + } else if (StrEqual(s, "set")) { + command = Command::Set; } - } else if (!inEcho && logicalVisibleChars == sc.LengthCurrent()) { + } else if (keywordLists[1]->InList(s)) { + command = Command::Argument; + parenBefore = parenCount; + sc.ChangeState(SCE_BAT_WORD); + } else if (logicalVisibleChars == sc.LengthCurrent()) { + command = Command::Argument; + parenBefore = parenCount; sc.ChangeState(SCE_BAT_COMMAND); + if (sc.ch == ':') { + sc.Forward(); + } } - if (sc.state != SCE_BAT_LABEL) { - sc.SetState(SCE_BAT_DEFAULT); - } + sc.SetState(SCE_BAT_DEFAULT); } } break; @@ -277,28 +313,18 @@ void ColouriseBatchDoc(Sci_PositionU startPos, Sci_Position length, int initStyl } } if (varQuoteChar == '\0') { - if (nestedState.empty()) { - sc.SetState(SCE_BAT_DEFAULT); - } else { - sc.SetState(nestedState.back()); - nestedState.pop_back(); - continue; - } + sc.SetState(outerStyle); + continue; } break; case SCE_BAT_STRINGDQ: case SCE_BAT_STRINGSQ: case SCE_BAT_STRINGBT: - if (GetBatEscapeLen(sc, escapeLen)) { - nestedState.push_back(sc.state); - sc.SetState(SCE_BAT_ESCAPECHAR); - sc.Forward(escapeLen); + if (DetectBatchEscapeChar(sc, outerStyle)) { + // nop } else if (sc.ch == '%' || sc.ch == '!') { - nestedState.push_back(sc.state); - if (!DetectBatVariable(sc, varQuoteChar)) { - nestedState.pop_back(); - } + DetectBatchVariable(sc, outerStyle, varQuoteChar); } else if ((sc.state == SCE_BAT_STRINGDQ && sc.ch == '\"') || (sc.state == SCE_BAT_STRINGSQ && sc.ch == '\'') || (sc.state == SCE_BAT_STRINGBT && sc.ch == '`')) { @@ -312,26 +338,18 @@ void ColouriseBatchDoc(Sci_PositionU startPos, Sci_Position length, int initStyl } else if (sc.ch == '\"') { nestedState.push_back(sc.state); sc.SetState(SCE_BAT_STRINGDQ); - } else if (IsSingleQuotedString(sc.ch, sc.chPrev, sc.chNext)) { - nestedState.push_back(sc.state); - sc.SetState(SCE_BAT_STRINGSQ); - } else if (sc.ch == '`') { - nestedState.push_back(sc.state); - sc.SetState(SCE_BAT_STRINGBT); + } else if (sc.state == SCE_BAT_STRINGDQ && sc.ch == '=' && command == Command::Set) { + command = Command::Argument; + sc.SetState(SCE_BAT_OPERATOR); + sc.ForwardSetState(SCE_BAT_STRINGDQ); + continue; } break; case SCE_BAT_ESCAPECHAR: - if (GetBatEscapeLen(sc, escapeLen)) { - sc.Forward(escapeLen); - } else { - if (nestedState.empty()) { - sc.SetState(SCE_BAT_DEFAULT); - } else { - sc.SetState(nestedState.back()); - nestedState.pop_back(); - continue; - } + if (!DetectBatchEscapeChar(sc, outerStyle)) { + sc.SetState(outerStyle); + continue; } break; @@ -363,59 +381,80 @@ void ColouriseBatchDoc(Sci_PositionU startPos, Sci_Position length, int initStyl sc.SetState(SCE_BAT_NOT_BATCH); levelNext++; } - } else if (GetBatEscapeLen(sc, escapeLen)) { - sc.SetState(SCE_BAT_ESCAPECHAR); - sc.Forward(escapeLen); + } else if (DetectBatchEscapeChar(sc, outerStyle) || + ((sc.ch == '%' || sc.ch == '!') && DetectBatchVariable(sc, outerStyle, varQuoteChar))) { + // nop } else if (sc.ch == '\"') { sc.SetState(SCE_BAT_STRINGDQ); - } else if (IsSingleQuotedString(sc.ch, sc.chPrev, sc.chNext)) { - sc.SetState(SCE_BAT_STRINGSQ); - } else if (sc.ch == '`') { - sc.SetState(SCE_BAT_STRINGBT); - } else if ((sc.ch == '%' || sc.ch == '!') && DetectBatVariable(sc, varQuoteChar)) { - // nop + } else if ((sc.ch == '\'' || sc.ch == '`') && (chPrevNonWhite == '(' && stylePrevNonWhite == SCE_BAT_OPERATOR)) { + sc.SetState((sc.ch == '\'') ? SCE_BAT_STRINGSQ : SCE_BAT_STRINGBT); } else if (sc.ch == '(' || sc.ch == ')') { sc.SetState(SCE_BAT_OPERATOR); - if (!inEcho || parenCount > 0) { + if (command != Command::Echo || parenCount > 0) { if (sc.ch == '(') { parenCount++; levelNext++; - } else { + } else if (parenCount > 0) { parenCount--; levelNext--; - inEcho = false; + if (parenCount < parenBefore) { + command = Command::None; + } } } - } else if (IsBatOp(sc.ch, inEcho)) { + } else if (IsBatchOperator(sc.ch, command)) { sc.SetState(SCE_BAT_OPERATOR); - if (sc.ch == '>') { + switch (sc.ch) { + case '|': + case '&': + if (sc.ch == sc.chNext) { + // cmd1 || cmd2, cmd1 && cmd2 + sc.Forward(); + } + sc.ForwardSetState(SCE_BAT_DEFAULT); + logicalVisibleChars = 0; + command = Command::None; + continue; + + case '>': if (sc.chNext == '&') { // output redirect: 2>&1 sc.Forward(); } - inEcho = false; - } else if (sc.ch == '|' || sc.ch == '&') { // pipe - if (sc.ch == sc.chNext) { // cmd1 || cmd2, cmd1 && cmd2 - sc.Forward(); + command = Command::None; + break; + + case '=': + if (command == Command::Set) { + command = Command::Argument; } - if (IsWordStart(sc.chNext)) { - sc.ForwardSetState(SCE_BAT_IDENTIFIER); - } else { - sc.ForwardSetState(SCE_BAT_DEFAULT); + break; + + case '@': + if (logicalVisibleChars == 0 && IsFileNameChar(sc.chNext)) { + sc.Forward(); + if (sc.MatchIgnoreCase("rem") && !IsGraphic(sc.GetRelative(3))) { + sc.ChangeState(SCE_BAT_COMMENT); + } else { + sc.SetState(SCE_BAT_IDENTIFIER); + } } - logicalVisibleChars = 0; - inEcho = false; - continue; + break; + + default: + break; } - if (logicalVisibleChars == 0 && sc.ch == '@' && IsWordStart(sc.chNext)) { + } else if (command == Command::Call && sc.ch == ':') { + command = Command::Argument; + if (IsDrive(sc.chPrev, sc.chNext)) { sc.Forward(); - if (sc.MatchIgnoreCase("rem") && !IsGraphic(sc.GetRelative(3))) { - sc.ChangeState(SCE_BAT_COMMENT); - } else { - sc.SetState(SCE_BAT_IDENTIFIER); - } + } else { + sc.SetState(SCE_BAT_LABEL); } - } else if (IsWordStart(sc.ch)) { + } else if (command == Command::Goto && IsGraphic(sc.ch)) { + command = Command::Argument; + sc.SetState(SCE_BAT_LABEL); + } else if ((logicalVisibleChars == 0 || command == Command::None) && IsFileNameChar(sc.ch)) { sc.SetState(SCE_BAT_IDENTIFIER); } } @@ -423,28 +462,37 @@ void ColouriseBatchDoc(Sci_PositionU startPos, Sci_Position length, int initStyl if (!isspacechar(sc.ch)) { logicalVisibleChars++; lineVisibleChars++; + if (sc.state != SCE_BAT_DEFAULT) { + chPrevNonWhite = sc.ch; + stylePrevNonWhite = sc.state; + } } if (sc.atLineEnd) { - const int chPrev = sc.LineLastChar(); varQuoteChar = '\0'; + outerStyle = SCE_BAT_DEFAULT; - int lineState = lineVisibleChars ? 0 : BatchLineStateMaskEmptyLine; - lineVisibleChars = 0; - if (chPrev == '^' && sc.state != SCE_BAT_COMMENT) { - lineState = BatchLineStateLineContinuation | (logicalVisibleChars ? BatchLineStateMaskVisibleChars : 0); + int lineState = parenCount << 8; + if (stylePrevNonWhite == SCE_BAT_LINE_CONTINUATION) { + lineState |= BatchLineStateLineContinuation; + lineState |= static_cast(command) << 4; + sc.SetState(SCE_BAT_DEFAULT); } else { - inEcho = false; + lineState |= lineVisibleChars ? 0 : BatchLineStateMaskEmptyLine; + command = Command::None; logicalVisibleChars = 0; } - lineState |= parenCount << 8; - if (sc.state == SCE_BAT_STRINGDQ || sc.state == SCE_BAT_STRINGSQ || sc.state == SCE_BAT_STRINGBT) { - if (sc.state != SCE_BAT_STRINGDQ && (chPrev == '^' || chPrev == '|' || chPrev == '&')) { - if (!nestedState.empty()) { - lineState |= PackLineState(nestedState) << 16; + lineVisibleChars = 0; + if (IsStringStyle(sc.state)) { + if (sc.state == SCE_BAT_STRINGDQ) { + if (nestedState.empty()) { + sc.SetState(SCE_BAT_DEFAULT); + } else { + sc.SetState(nestedState.back()); + nestedState.pop_back(); } - } else { - nestedState.clear(); - sc.SetState(SCE_BAT_DEFAULT); + } + if (!nestedState.empty()) { + lineState |= PackLineState(nestedState) << 16; } } styler.SetLineState(sc.currentLine, lineState); diff --git a/src/EditLexers/stlBatch.c b/src/EditLexers/stlBatch.c index 97f7dfb575..7aa618535b 100644 --- a/src/EditLexers/stlBatch.c +++ b/src/EditLexers/stlBatch.c @@ -3,13 +3,15 @@ static KEYWORDLIST Keywords_Batch = {{ //++Autogenerated -- start of section automatically generated -"assoc break call cd chdir cls cmdextversion color copy " -"date defined del dir disabledelayedexpansion disableextensions do " -"echo echo. else enabledelayedexpansion enableextensions endlocal equ erase errorlevel exist exit for ftype geq goto gtr " -"if in leq lss md mkdir mklink move neq not nul path pause popd prompt pushd rd rem ren rename rmdir " -"set setlocal shift start time title type ver verify vol " +"break call cls cmdextversion defined disabledelayedexpansion disableextensions do " +"echo echo. else enabledelayedexpansion enableextensions endlocal equ errorlevel exist exit for geq goto gtr if in " +"leq lss neq not nul pause popd rem set setlocal shift title ver verify " -, // 1 system commands +, // 1 internal commands +"assoc cd chdir color copy date del dir erase ftype md mkdir mklink move path prompt pushd rd ren rename rmdir start " +"time type vol " + +, // 2 system commands "active add append arp assign at atmadm attach-vdisk attrib attributes auditpol autochk autoconv autofmt automount " "bcdboot bcdedit bdehdcfg begin bitsadmin bootcfg " "cacls certreq certutil change chcp chglogon chgport chgusr chkdsk chkntfs choice cipher clean cleanmgr clip " @@ -36,7 +38,7 @@ static KEYWORDLIST Keywords_Batch = {{ "unexpose uniqueid unlodctr verifier vssadmin " "waitfor wbadmin wdsutil wecutil wevtutil where whoami winnt winnt32 winpop winrs winsat wmic writer wscript xcopy " -, // 2 upper case keywords / commands +, // 3 upper case keywords / commands "ASSOC ATTRIB BCDEDIT BREAK " "CACLS CALL CD CHCP CHDIR CHKDSK CHKNTFS CLS CMD CMDEXTVERSION COLOR COMP COMPACT CONVERT COPY CmdExtVersion " "DATE DEFINED DEL DIR DISABLEDELAYEDEXPANSION DISABLEEXTENSIONS DISKPART DO DOSKEY DRIVERQUERY " @@ -49,7 +51,7 @@ static KEYWORDLIST Keywords_Batch = {{ "SC SCHTASKS SET SETLOCAL SHIFT SHUTDOWN SORT START SUBST SYSTEMINFO SetLocal TASKKILL TASKLIST TIME TITLE TREE TYPE " "VER VERIFY VOL WMIC XCOPY " -, // 3 environment variables +, // 4 environment variables "ALLUSERSPROFILE APPDATA AllUsersProfile AppData " "CMDCMDLINE COMPUTERNAME CmdCmdLine ComSpec CommonProgramFiles CommonProgramFiles(x86) CommonProgramW6432 ComputerName " "DriverData ExitCode ExitCodeAscii HIGHESTNUMANODENUMBER HOMEDRIVE HOMEPATH HighestNumaNodeNumber HomeDrive HomePath " @@ -61,7 +63,7 @@ static KEYWORDLIST Keywords_Batch = {{ "USERDOMAIN USERDOMAIN_ROAMINGPROFILE USERNAME USERPROFILE UserDomain UserDomain_RoamingProfile UserName UserProfile " "WINDIR WinDir __APPDIR__ __AppDir__ __CD__ random windir " -, // 4 command options +, // 5 command options "ABOVENORMAL AFFINITY BELOWNORMAL EOF HIGH LOW MAX MIN NODE NORMAL REALTIME SEPARATE SHARED WAIT " "abovenormal addenctypeattr addfile addfileset addfilewithranges addhosttorealmmap addkdc addkpasswd addrealmflags addsw " "affinity alert alias all analyze and api ascii autounlock " @@ -101,7 +103,7 @@ static KEYWORDLIST Keywords_Batch = {{ "unload unlock unset update upgrade usebackq user usn util " "validate variables vc vdisk verbose version versions view volume wait wim wipefreespace wrap writers ws2008 " -, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL //--Autogenerated -- end of section automatically generated }}; @@ -113,7 +115,7 @@ static EDITSTYLE Styles_Batch[] = { { SCE_BAT_STRINGDQ, NP2StyleX_DoubleQuotedString, L"fore:#008080" }, { SCE_BAT_STRINGSQ, NP2StyleX_SingleQuotedString, L"fore:#C08000" }, { SCE_BAT_STRINGBT, NP2StyleX_Backticks, L"fore:#F08000" }, - { SCE_BAT_ESCAPECHAR, NP2StyleX_EscapeSequence, L"fore:#FF0000" }, + { MULTI_STYLE(SCE_BAT_ESCAPECHAR, SCE_BAT_LINE_CONTINUATION, 0, 0), NP2StyleX_EscapeSequence, L"fore:#FF0000" }, { SCE_BAT_VARIABLE, NP2StyleX_Variable, L"fore:#003CE6; back:#FFF1A8" }, { SCE_BAT_OPERATOR, NP2StyleX_Operator, L"fore:#B000B0" }, { MULTI_STYLE(SCE_BAT_LABEL, SCE_BAT_LABEL_LINE, 0, 0), NP2StyleX_Label, L"fore:#C80000; back:#F4F4F4; eolfilled" }, diff --git a/src/Styles.c b/src/Styles.c index fc11c7f2f8..171238303e 100644 --- a/src/Styles.c +++ b/src/Styles.c @@ -1300,10 +1300,10 @@ void Style_UpdateLexerKeywordAttr(LPCEDITLEXER pLexNew) { attr[4] = KeywordAttr_NoLexer; // misc break; case NP2LEX_BATCH: - attr[1] = KeywordAttr_NoLexer; // system commands - attr[2] = KeywordAttr_NoLexer; // upper case keywords / commands - attr[3] = KeywordAttr_NoLexer; // environment variables - attr[4] = KeywordAttr_NoLexer; // command options + attr[2] = KeywordAttr_NoLexer; // system commands + attr[3] = KeywordAttr_NoLexer; // upper case keywords / commands + attr[4] = KeywordAttr_NoLexer; // environment variables + attr[5] = KeywordAttr_NoLexer; // command options break; case NP2LEX_CMAKE: attr[6] = KeywordAttr_NoLexer; // long properties diff --git a/tools/KeywordCore.py b/tools/KeywordCore.py index 8209872c87..54de5985bc 100644 --- a/tools/KeywordCore.py +++ b/tools/KeywordCore.py @@ -315,7 +315,7 @@ def parse_batch_api_file(path): 'options': [], } for key, doc in sections: - if key == 'keywords': + if key in ('keywords', 'internal command'): items = doc.split() keywordMap['upper case keywords'].extend(items) keywordMap[key] = [item.lower() for item in items] @@ -342,6 +342,7 @@ def parse_batch_api_file(path): RemoveDuplicateKeyword(keywordMap, [ 'keywords', + 'internal command', 'system commands', 'upper case keywords', 'environment variables', @@ -349,6 +350,7 @@ def parse_batch_api_file(path): ]) return [ ('keywords', keywordMap['keywords'], KeywordAttr.Default), + ('internal commands', keywordMap['internal command'], KeywordAttr.Default), ('system commands', keywordMap['system commands'], KeywordAttr.NoLexer), ('upper case keywords / commands', keywordMap['upper case keywords'], KeywordAttr.NoLexer), ('environment variables', keywordMap['environment variables'], KeywordAttr.NoLexer), diff --git a/tools/lang/Batch.bat b/tools/lang/Batch.bat index 907477975e..3779132903 100644 --- a/tools/lang/Batch.bat +++ b/tools/lang/Batch.bat @@ -3,26 +3,16 @@ ::! keywords =========================================================== :: built-in command, output from help command -ASSOC BREAK CALL -CD -CHDIR CLS -COLOR -COPY -DATE -DEL -DIR ECHO ECHO. ENDLOCAL EndLocal -ERASE EXIT FOR IN DO -FTYPE GOTO IF ELSE @@ -34,20 +24,9 @@ IF ERRORLEVEL ErrorLevel CMDEXTVERSION CmdExtVersion -MD -MKDIR -MKLINK -MOVE -PATH PAUSE POPD -PROMPT -PUSHD -RD REM -REN -RENAME -RMDIR SET SETLOCAL SetLocal ENABLEEXTENSIONS EnableExtensions @@ -55,17 +34,41 @@ SETLOCAL SetLocal ENABLEDELAYEDEXPANSION EnableDelayedExpansion DISABLEDELAYEDEXPANSION DisableDelayedExpansion SHIFT -START -TIME TITLE -TYPE VER VERIFY -VOL :: other NUL +::! internal command ======================================================= +:: built-in command with arguments +ASSOC +CD +CHDIR +COLOR +COPY +DATE +DEL +DIR +ERASE +FTYPE +MD +MKDIR +MKLINK +MOVE +PATH +PROMPT +PUSHD +RD +REN +RENAME +RMDIR +START +TIME +TYPE +VOL + ::! external command ======================================================= :: output from help command :: *.com