Repository: alecthomas/chroma
Pull request #1026 (2024-12-02 18:18:35)

Import NSIS Lexer from Pygments
This imports `pygments.lexers.installers.NSISLexer` from Pygmnets into Chroma

Implements #459

alecthomas: Thanks!

Patch:
diff --git a/README.md b/README.md
index 7c35585..735b556 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@ translators for Pygments lexers and styles.
 |   K    | Kotlin                                                                                                                                                                                                                                              |
 |   L    | Lighttpd configuration file, LLVM, Lua                                                                                                                                                                                                              |
 |   M    | Makefile, Mako, markdown, Mason, Materialize SQL dialect, Mathematica, Matlab, MCFunction, Meson, Metal, MiniZinc, MLIR, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL                                                                          |
-|   N    | NASM, Natural, Newspeak, Nginx configuration file, Nim, Nix                                                                                                                                                                                         |
+|   N    | NASM, Natural, Newspeak, Nginx configuration file, Nim, Nix, NSIS                                                                                                                                                                                   |
 |   O    | Objective-C, OCaml, Octave, Odin, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode                                                                                                                                                                  |
 |   P    | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Plutus Core, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerQuery, PowerShell, Prolog, PromQL, Promela, properties, Protocol Buffer, PRQL, PSL, Puppet, Python, Python 2 |
 |   Q    | QBasic, QML                                                                                                                                                                                                                                         |
diff --git a/lexers/embedded/nsis.xml b/lexers/embedded/nsis.xml
new file mode 100644
index 0000000..6c3a7be
--- /dev/null
+++ b/lexers/embedded/nsis.xml
@@ -0,0 +1,59 @@
+<lexer>
+  <config>
+    <name>NSIS</name>
+    <alias>nsis</alias>
+    <alias>nsi</alias>
+    <alias>nsh</alias>
+    <filename>*.nsi</filename>
+    <filename>*.nsh</filename>
+    <mime_type>text/x-nsis</mime_type>
+    <case_insensitive>true</case_insensitive>
+    <not_multiline>true</not_multiline>
+  </config>
+  <rules>
+    <state name="root">
+      <rule pattern="([;#].*)(\n)"><bygroups><token type="Comment"/><token type="TextWhitespace"/></bygroups></rule>
+      <rule pattern="&#x27;.*?&#x27;"><token type="LiteralStringSingle"/></rule>
+      <rule pattern="&quot;"><token type="LiteralStringDouble"/><push state="str_double"/></rule>
+      <rule pattern="`"><token type="LiteralStringBacktick"/><push state="str_backtick"/></rule>
+      <rule><include state="macro"/></rule>
+      <rule><include state="interpol"/></rule>
+      <rule><include state="basic"/></rule>
+      <rule pattern="\$\{[a-z_|][\w|]*\}"><token type="KeywordPseudo"/></rule>
+      <rule pattern="/[a-z_]\w*"><token type="NameAttribute"/></rule>
+      <rule pattern="\s+"><token type="TextWhitespace"/></rule>
+      <rule pattern="[\w.]+"><token type="Text"/></rule>
+    </state>
+    <state name="basic">
+      <rule pattern="(\n)(Function)(\s+)([._a-z][.\w]*)\b"><bygroups><token type="TextWhitespace"/><token type="Keyword"/><token type="TextWhitespace"/><token type="NameFunction"/></bygroups></rule>
+      <rule pattern="\b([_a-z]\w*)(::)([a-z][a-z0-9]*)\b"><bygroups><token type="KeywordNamespace"/><token type="Punctuation"/><token type="NameFunction"/></bygroups></rule>
+      <rule pattern="\b([_a-z]\w*)(:)"><bygroups><token type="NameLabel"/><token type="Punctuation"/></bygroups></rule>
+      <rule pattern="(\b[ULS]|\B)([!&lt;&gt;=]?=|\&lt;\&gt;?|\&gt;)\B"><token type="Operator"/></rule>
+      <rule pattern="[|+-]"><token type="Operator"/></rule>
+      <rule pattern="\\"><token type="Punctuation"/></rule>
+      <rule pattern="\b(Abort|Add(?:BrandingImage|Size)|Allow(?:RootDirInstall|SkipFiles)|AutoCloseWindow|BG(?:Font|Gradient)|BrandingText|BringToFront|Call(?:InstDLL)?|(?:Sub)?Caption|ChangeUI|CheckBitmap|ClearErrors|CompletedText|ComponentText|CopyFiles|CRCCheck|Create(?:Directory|Font|Shortcut)|Delete(?:INI(?:Sec|Str)|Reg(?:Key|Value))?|DetailPrint|DetailsButtonText|Dir(?:Show|Text|Var|Verify)|(?:Disabled|Enabled)Bitmap|EnableWindow|EnumReg(?:Key|Value)|Exch|Exec(?:Shell|Wait)?|ExpandEnvStrings|File(?:BufSize|Close|ErrorText|Open|Read(?:Byte)?|Seek|Write(?:Byte)?)?|Find(?:Close|First|Next|Window)|FlushINI|Function(?:End)?|Get(?:CurInstType|CurrentAddress|DlgItem|DLLVersion(?:Local)?|ErrorLevel|FileTime(?:Local)?|FullPathName|FunctionAddress|InstDirError|LabelAddress|TempFileName)|Goto|HideWindow|Icon|If(?:Abort|Errors|FileExists|RebootFlag|Silent)|InitPluginsDir|Install(?:ButtonText|Colors|Dir(?:RegKey)?)|Inst(?:ProgressFlags|Type(?:[GS]etText)?)|Int(?:CmpU?|Fmt|Op)|IsWindow|LangString(?:UP)?|License(?:BkColor|Data|ForceSelection|LangString|Text)|LoadLanguageFile|LockWindow|Log(?:Set|Text)|MessageBox|MiscButtonText|Name|Nop|OutFile|(?:Uninst)?Page(?:Ex(?:End)?)?|PluginDir|Pop|Push|Quit|Read(?:(?:Env|INI|Reg)Str|RegDWORD)|Reboot|(?:Un)?RegDLL|Rename|RequestExecutionLevel|ReserveFile|Return|RMDir|SearchPath|Section(?:Divider|End|(?:(?:Get|Set)(?:Flags|InstTypes|Size|Text))|Group(?:End)?|In)?|SendMessage|Set(?:AutoClose|BrandingImage|Compress(?:ionLevel|or(?:DictSize)?)?|CtlColors|CurInstType|DatablockOptimize|DateSave|Details(?:Print|View)|Error(?:s|Level)|FileAttributes|Font|OutPath|Overwrite|PluginUnload|RebootFlag|ShellVarContext|Silent|StaticBkColor)|Show(?:(?:I|Uni)nstDetails|Window)|Silent(?:Un)?Install|Sleep|SpaceTexts|Str(?:CmpS?|Cpy|Len)|SubSection(?:End)?|Uninstall(?:ButtonText|(?:Sub)?Caption|EXEName|Icon|Text)|UninstPage|Var|VI(?:AddVersionKey|ProductVersion)|WindowIcon|Write(?:INIStr|Reg(:?Bin|DWORD|(?:Expand)?Str)|Uninstaller)|XPStyle)\b"><token type="Keyword"/></rule>
+      <rule pattern="\b(CUR|END|(?:FILE_ATTRIBUTE_)?(?:ARCHIVE|HIDDEN|NORMAL|OFFLINE|READONLY|SYSTEM|TEMPORARY)|HK(CC|CR|CU|DD|LM|PD|U)|HKEY_(?:CLASSES_ROOT|CURRENT_(?:CONFIG|USER)|DYN_DATA|LOCAL_MACHINE|PERFORMANCE_DATA|USERS)|ID(?:ABORT|CANCEL|IGNORE|NO|OK|RETRY|YES)|MB_(?:ABORTRETRYIGNORE|DEFBUTTON[1-4]|ICON(?:EXCLAMATION|INFORMATION|QUESTION|STOP)|OK(?:CANCEL)?|RETRYCANCEL|RIGHT|SETFOREGROUND|TOPMOST|USERICON|YESNO(?:CANCEL)?)|SET|SHCTX|SW_(?:HIDE|SHOW(?:MAXIMIZED|MINIMIZED|NORMAL))|admin|all|auto|both|bottom|bzip2|checkbox|colored|current|false|force|hide|highest|if(?:diff|newer)|lastused|leave|left|listonly|lzma|nevershow|none|normal|off|on|pop|push|radiobuttons|right|show|silent|silentlog|smooth|textonly|top|true|try|user|zlib)\b"><token type="NameConstant"/></rule>
+    </state>
+    <state name="macro">
+      <rule pattern="\!(addincludedir(?:dir)?|addplugindir|appendfile|cd|define|delfilefile|echo(?:message)?|else|endif|error|execute|if(?:macro)?n?(?:def)?|include|insertmacro|macro(?:end)?|packhdr|search(?:parse|replace)|system|tempfilesymbol|undef|verbose|warning)\b"><token type="CommentPreproc"/></rule>
+    </state>
+    <state name="interpol">
+      <rule pattern="\$(R?[0-9])"><token type="NameBuiltinPseudo"/></rule>
+      <rule pattern="\$(ADMINTOOLS|APPDATA|CDBURN_AREA|COOKIES|COMMONFILES(?:32|64)|DESKTOP|DOCUMENTS|EXE(?:DIR|FILE|PATH)|FAVORITES|FONTS|HISTORY|HWNDPARENT|INTERNET_CACHE|LOCALAPPDATA|MUSIC|NETHOOD|PICTURES|PLUGINSDIR|PRINTHOOD|PROFILE|PROGRAMFILES(?:32|64)|QUICKLAUNCH|RECENT|RESOURCES(?:_LOCALIZED)?|SENDTO|SM(?:PROGRAMS|STARTUP)|STARTMENU|SYSDIR|TEMP(?:LATES)?|VIDEOS|WINDIR|\{NSISDIR\})"><token type="NameBuiltin"/></rule>
+      <rule pattern="\$(CMDLINE|INSTDIR|OUTDIR|LANGUAGE)"><token type="NameVariableGlobal"/></rule>
+      <rule pattern="\$[a-z_]\w*"><token type="NameVariable"/></rule>
+    </state>
+    <state name="str_double">
+      <rule pattern="&quot;"><token type="LiteralStringDouble"/><pop depth="1"/></rule>
+      <rule pattern="\$(\\[nrt&quot;]|\$)"><token type="LiteralStringEscape"/></rule>
+      <rule><include state="interpol"/></rule>
+      <rule pattern="[^&quot;]+"><token type="LiteralStringDouble"/></rule>
+    </state>
+    <state name="str_backtick">
+      <rule pattern="`"><token type="LiteralStringDouble"/><pop depth="1"/></rule>
+      <rule pattern="\$(\\[nrt&quot;]|\$)"><token type="LiteralStringEscape"/></rule>
+      <rule><include state="interpol"/></rule>
+      <rule pattern="[^`]+"><token type="LiteralStringDouble"/></rule>
+    </state>
+  </rules>
+</lexer>
\ No newline at end of file


Test patch:
diff --git a/lexers/testdata/nsis.actual b/lexers/testdata/nsis.actual
new file mode 100644
index 0000000..1a11d55
--- /dev/null
+++ b/lexers/testdata/nsis.actual
@@ -0,0 +1,37 @@
+!include "nsDialogs.nsh"
+!include "winmessages.nsh"
+!include "logiclib.nsh"
+OutFile "test.exe"
+
+Page Custom pre
+
+var dialog
+var hwnd
+var button
+
+Function pre
+	nsDialogs::Create 1018
+	Pop $dialog
+	${NSD_CreateCheckbox} 0 0 50 6 "Enable button below"
+		Pop $hwnd
+		${NSD_OnClick} $hwnd EnDisableButton
+
+	${NSD_CreateButton} 25 25 50 50 "Hello World"
+		Pop $button
+		EnableWindow $button 0 # start out disabled
+
+	nsDialogs::Show
+FunctionEnd
+
+Function EnDisableButton
+	Pop $hwnd
+	${NSD_GetState} $hwnd $0
+	${If} $0 == 1
+		EnableWindow $button 1
+	${Else}
+		EnableWindow $button 0
+	${EndIf}
+FunctionEnd
+
+Section ""
+SectionEnd
diff --git a/lexers/testdata/nsis.expected b/lexers/testdata/nsis.expected
new file mode 100644
index 0000000..deecc5e
--- /dev/null
+++ b/lexers/testdata/nsis.expected
@@ -0,0 +1,148 @@
+[
+  {"type":"CommentPreproc","value":"!include"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralStringDouble","value":"\"nsDialogs.nsh\""},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"CommentPreproc","value":"!include"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralStringDouble","value":"\"winmessages.nsh\""},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"CommentPreproc","value":"!include"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralStringDouble","value":"\"logiclib.nsh\""},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Keyword","value":"OutFile"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralStringDouble","value":"\"test.exe\""},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"Page"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"Custom"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"pre"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"var"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"dialog"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Keyword","value":"var"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"hwnd"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Keyword","value":"var"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"button"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"Function"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"pre"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"KeywordNamespace","value":"nsDialogs"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameFunction","value":"Create"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"1018"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"Keyword","value":"Pop"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameVariable","value":"$dialog"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"KeywordPseudo","value":"${NSD_CreateCheckbox}"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"0"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"0"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"50"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"6"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralStringDouble","value":"\"Enable button below\""},
+  {"type":"TextWhitespace","value":"\n\t\t"},
+  {"type":"Keyword","value":"Pop"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameVariable","value":"$hwnd"},
+  {"type":"TextWhitespace","value":"\n\t\t"},
+  {"type":"KeywordPseudo","value":"${NSD_OnClick}"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameVariable","value":"$hwnd"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"EnDisableButton"},
+  {"type":"TextWhitespace","value":"\n\n\t"},
+  {"type":"KeywordPseudo","value":"${NSD_CreateButton}"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"25"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"25"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"50"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"50"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralStringDouble","value":"\"Hello World\""},
+  {"type":"TextWhitespace","value":"\n\t\t"},
+  {"type":"Keyword","value":"Pop"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameVariable","value":"$button"},
+  {"type":"TextWhitespace","value":"\n\t\t"},
+  {"type":"Keyword","value":"EnableWindow"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameVariable","value":"$button"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"0"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Comment","value":"# start out disabled"},
+  {"type":"TextWhitespace","value":"\n\n\t"},
+  {"type":"KeywordNamespace","value":"nsDialogs"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameFunction","value":"Show"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Keyword","value":"FunctionEnd"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"Function"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"EnDisableButton"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"Keyword","value":"Pop"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameVariable","value":"$hwnd"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"KeywordPseudo","value":"${NSD_GetState}"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameVariable","value":"$hwnd"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameBuiltinPseudo","value":"$0"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"KeywordPseudo","value":"${If}"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameBuiltinPseudo","value":"$0"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"=="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"1"},
+  {"type":"TextWhitespace","value":"\n\t\t"},
+  {"type":"Keyword","value":"EnableWindow"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameVariable","value":"$button"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"1"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"KeywordPseudo","value":"${Else}"},
+  {"type":"TextWhitespace","value":"\n\t\t"},
+  {"type":"Keyword","value":"EnableWindow"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameVariable","value":"$button"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"0"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"KeywordPseudo","value":"${EndIf}"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Keyword","value":"FunctionEnd"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"Section"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralStringDouble","value":"\"\""},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Keyword","value":"SectionEnd"},
+  {"type":"TextWhitespace","value":"\n"}
+]