commit 3f8e51cee79257642e1a51faa56a3f2c369e45d3
parent 8310f2052636d70b6e8dc38c83c9b73012841177
Author: zeertzjq <zeertzjq@outlook.com>
Date: Mon, 1 Dec 2025 09:29:55 +0800
vim-patch:9.1.1936: filetype: Erlang lexical files are not recognized
Problem: filetype: Erlang lexical files are not recognized
Solution: Detect *.xrl files as leex filetype, include syntax and
filetype plugins (Jon Parise).
leex is the lexical analyzer generator for Erlang. Its input file format
follows a section-based structure and uses the `.xrl` file extension.
This initial work includes file detection, an ftplugin (which inherits
the Erlang configuration), and a syntax definition.
Reference:
- https://www.erlang.org/doc/apps/parsetools/leex.html
related: vim/vim#18819
closes: vim/vim#18832
https://github.com/vim/vim/commit/b087c5452b4c12bfc9e7f6cecbee34aab64c92d8
Co-authored-by: Jon Parise <jon@indelible.org>
Diffstat:
4 files changed, 79 insertions(+), 0 deletions(-)
diff --git a/runtime/ftplugin/leex.vim b/runtime/ftplugin/leex.vim
@@ -0,0 +1,14 @@
+" Vim ftplugin file
+" Language: Leex (Erlang Lexical Analyzer Generator)
+" Maintainer: Jon Parise <jon@indelible.org>
+" Last Change: 2025 Nov 29
+" Filenames: *.xrl
+"
+" References:
+" - https://www.erlang.org/doc/apps/parsetools/leex.html
+
+if exists('b:did_ftplugin')
+ finish
+endif
+
+runtime! ftplugin/erlang.vim
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
@@ -705,6 +705,7 @@ local extension = {
journal = 'ledger',
ldg = 'ledger',
ledger = 'ledger',
+ xrl = 'leex',
leo = 'leo',
less = 'less',
lex = 'lex',
diff --git a/runtime/syntax/leex.vim b/runtime/syntax/leex.vim
@@ -0,0 +1,63 @@
+" Vim syntax file
+" Language: Leex (Erlang Lexical Analyzer Generator)
+" Maintainer: Jon Parise <jon@indelible.org>
+" Last Change: 2025 Nov 30
+" Filenames: *.xrl
+"
+" References:
+" - https://www.erlang.org/doc/apps/parsetools/leex.html
+
+if exists('b:current_syntax')
+ finish
+endif
+
+syn include @leexErlang syntax/erlang.vim
+unlet! b:current_syntax
+
+syn match leexComment "%.*$" contains=@Spell display
+
+syn match leexRegexOperator "[|*+?]" contained display
+syn match leexRegexDelimiter "[()[\]]" contained display
+syn match leexRegexSpecial "[.^$\\]" contained display
+syn match leexRegexEscape '\\\%([bfnrtevsd]\|\o\{1,3}\|x\x\{2}\|x{\x\+}\|.\)' contained display
+syn match leexRegexRange "\[[^\]]*\]" contains=leexRegexDelimiter,leexRegexEscape contained display
+
+" Macro definitions: NAME = VALUE
+syn match leexMacroName "^\s*\zs\h\w*\ze\s\+=\s\+" contained nextgroup=leexMacroEquals skipwhite display
+syn match leexMacroEquals "=" contained nextgroup=leexMacroValue skipwhite display
+syn match leexMacroValue "\S\+" contained contains=leexRegexOperator,leexRegexDelimiter,leexRegexSpecial,leexRegexEscape,leexRegexRange,leexMacroRef display
+syn match leexMacroRef "{\h\w*}" contained display
+
+" Rule definitions: <Regexp> : <Erlang code>.
+syn match leexRuleRegex "^\s*\zs[^%].\{-}\ze\s\+:" contained contains=leexRegexOperator,leexRegexDelimiter,leexRegexSpecial,leexRegexEscape,leexRegexRange,leexMacroRef nextgroup=leexRuleColon skipwhite display
+syn match leexRuleColon ":" contained nextgroup=leexRuleCode skipwhite skipnl display
+syn region leexRuleCode start="" end="\.\s*\%(%.*\)\?$" skip="^\s*%.*$" contained contains=@leexErlang keepend skipnl skipwhite
+
+" Sections
+syn match leexHeading "^\%(Definitions\|Rules\|Erlang code\)\.$" contained display
+syn region leexDefinitions start="^Definitions\.$" end="^[A-Z][A-Za-z ]*\.$"me=s-1 end="\%$" keepend fold
+ \ contains=leexHeading,leexComment,leexMacroName
+syn region leexRules start="^Rules\.$" end="^[A-Z][A-Za-z ]*\.$"me=s-1 end="\%$" keepend fold
+ \ contains=leexHeading,leexComment,leexRuleRegex
+syn region leexErlangCode start="^Erlang code\.$" end="^[A-Z][A-Za-z ]*\.$"me=s-1 end="\%$" keepend fold
+ \ contains=leexHeading,@leexErlang
+
+hi def link leexComment Comment
+hi def link leexHeading PreProc
+
+hi def link leexRegexOperator Operator
+hi def link leexRegexDelimiter Delimiter
+hi def link leexRegexSpecial Special
+hi def link leexRegexRange String
+hi def link leexRegexEscape SpecialChar
+
+hi def link leexMacroName Identifier
+hi def link leexMacroEquals Operator
+hi def link leexMacroValue String
+hi def link leexMacroRef Macro
+
+hi def link leexRuleColon Operator
+
+syn sync fromstart
+
+let b:current_syntax = 'leex'
diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim
@@ -437,6 +437,7 @@ func s:GetFilenameChecks() abort
\ 'ldif': ['file.ldif'],
\ 'lean': ['file.lean'],
\ 'ledger': ['file.ldg', 'file.ledger', 'file.journal'],
+ \ 'leex': ['file.xrl'],
\ 'leo': ['file.leo'],
\ 'less': ['file.less'],
\ 'lex': ['file.lex', 'file.l', 'file.lxx', 'file.l++'],