Commit: patch 9.1.1936: filetype: Erlang lexical files are not recognized

1 view
Skip to first unread message

Christian Brabandt

unread,
Nov 30, 2025, 10:00:40 AM (10 days ago) Nov 30
to vim...@googlegroups.com
patch 9.1.1936: filetype: Erlang lexical files are not recognized

Commit: https://github.com/vim/vim/commit/b087c5452b4c12bfc9e7f6cecbee34aab64c92d8
Author: Jon Parise <j...@indelible.org>
Date: Sun Nov 30 14:45:48 2025 +0000

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: #18819
closes: #18832

Signed-off-by: Jon Parise <j...@indelible.org>
Signed-off-by: Csaba Hoch <csaba...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS
index 5eb9a3560..ff59e3d65 100644
--- a/.github/MAINTAINERS
+++ b/.github/MAINTAINERS
@@ -221,6 +221,7 @@ runtime/ftplugin/kivy.vim @ribru17
runtime/ftplugin/kotlin.vim @udalov
runtime/ftplugin/lc.vim @ribru17
runtime/ftplugin/ldapconf.vim @ribru17
+runtime/ftplugin/leex.vim @jparise
runtime/ftplugin/leo.vim @ribru17
runtime/ftplugin/less.vim @genoma
runtime/ftplugin/lex.vim @ribru17
@@ -550,6 +551,7 @@ runtime/syntax/kivy.vim @prophittcorey
runtime/syntax/kotlin.vim @udalov
runtime/syntax/kdl.vim @imsnif @jiangyinzuo
runtime/syntax/krl.vim @KnoP-01
+runtime/syntax/leex.vim @jparise
runtime/syntax/less.vim @genoma
runtime/syntax/lf.vim @andis-sprinkis
runtime/syntax/liquid.vim @tpope
diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim
index df377dd69..4312e6dca 100644
--- a/runtime/autoload/dist/ft.vim
+++ b/runtime/autoload/dist/ft.vim
@@ -2226,6 +2226,8 @@ const ft_from_ext = {
"ldg": "ledger",
"ledger": "ledger",
"journal": "ledger",
+ # Leex
+ "xrl": "leex",
# Leo
"leo": "leo",
# Less
diff --git a/runtime/ftplugin/leex.vim b/runtime/ftplugin/leex.vim
new file mode 100644
index 000000000..1d5ce06ff
--- /dev/null
+++ b/runtime/ftplugin/leex.vim
@@ -0,0 +1,14 @@
+" Vim ftplugin file
+" Language: Leex (Erlang Lexical Analyzer Generator)
+" Maintainer: Jon Parise <j...@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/syntax/leex.vim b/runtime/syntax/leex.vim
new file mode 100644
index 000000000..05f92c374
--- /dev/null
+++ b/runtime/syntax/leex.vim
@@ -0,0 +1,63 @@
+" Vim syntax file
+" Language: Leex (Erlang Lexical Analyzer Generator)
+" Maintainer: Jon Parise <j...@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/runtime/syntax/testdir/dumps/leex_00.dump b/runtime/syntax/testdir/dumps/leex_00.dump
new file mode 100644
index 000000000..72d4642aa
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/leex_00.dump
@@ -0,0 +1,20 @@
+>%+0#0000e05#ffffff0| |H|e|a|d|e|r| |c|o|m@1|e|n|t| +0#0000000&@58
+|%+0#0000e05&@1| |H|e|a|d|e|r| |c|o|m@1|e|n|t| +0#0000000&@57
+|%+0#0000e05&@2| |H|e|a|d|e|r| |c|o|m@1|e|n|t| +0#0000000&@56
+@75
+|D+0#e000e06&|e|f|i|n|i|t|i|o|n|s|.| +0#0000000&@62
+|f+0#00e0e07&|l|o|a|t|s| +0#0000000&@1|=+0#af5f00255&| +0#0000000&|(+0#e000e06&|\|+||+0#af5f00255&|-+0#e000002&|)+0#e000e06&|?+0#af5f00255&|[+0#e000e06&|0+0#e000002&|-|9|]+0#e000e06&|++0#af5f00255&|\+0#e000e06&|.|[|0+0#e000002&|-|9|]+0#e000e06&|++0#af5f00255&|(+0#e000e06&@1|E+0#e000002&||+0#af5f00255&|e+0#e000002&|)+0#e000e06&|(|\|+||+0#af5f00255&|-+0#e000002&|)+0#e000e06&|?+0#af5f00255&|[+0#e000e06&|0+0#e000002&|-|9|]+0#e000e06&|++0#af5f00255&|)+0#e000e06&|?+0#af5f00255&| +0#0000000&@22
+|D+0#00e0e07&| +0#0000000&@6|=+0#af5f00255&| +0#0000000&|[+0#e000e06&|0+0#e000002&|-|9|]+0#e000e06&| +0#0000000&@59
+|A+0#00e0e07&| +0#0000000&@6|=+0#af5f00255&| +0#0000000&|(+0#e000e06&|{|D|}||+0#af5f00255&|_+0#e000002&||+0#af5f00255&|@+0#e000002&|)+0#e000e06&| +0#0000000&@55
+|W+0#00e0e07&|S| +0#0000000&@5|=+0#af5f00255&| +0#0000000&|(+0#e000e06&|[|\|0@2|-+0#e000002&|\+0#e000e06&|s|]||+0#af5f00255&|%+0#e000002&|.+0#e000e06&|*+0#af5f00255&|)+0#e000e06&| +0#0000000&|%+0#0000e05&| |w|h|i|t|e|s|p|a|c|e| +0#0000000&@36
+@75
+|R+0#e000e06&|u|l|e|s|.| +0#0000000&@68
+|{+0#e000e06&|D|}|++0#af5f00255&| +0#0000000&|:+0#af5f00255&| +0#0000000&@68
+@2|%+0#0000e05&| |C|o|m@1|e|n|t| +0#0000000&@63
+@2|{+0#e000e06&|t+0#e000002&|o|k|e|n|,+0#0000000&|{+0#e000e06&|i+0#e000002&|n|t|e|g|e|r|,+0#0000000&|T+0#00e0e07&|o|k|e|n|L|i|n|e|,+0#0000000&|l+0#00e0e07&|i|s|t|_|t|o|_|i|n|t|e|g|e|r|(+0#0000000&|T+0#00e0e07&|o|k|e|n|C|h|a|r|s|)+0#0000000&|}+0#e000e06&@1|.+0#0000000&| @16
+|{+0#e000e06&|D|}|++0#af5f00255&|\+0#e000e06&|.|{|D|}|++0#af5f00255&|(+0#e000e06&@1|E+0#0000000&||+0#af5f00255&|e+0#0000000&|)+0#e000e06&|(|\|+||+0#af5f00255&|\+0#e000e06&|-|)|?+0#af5f00255&|{+0#e000e06&|D|}|++0#af5f00255&|)+0#e000e06&|?+0#af5f00255&| +0#0000000&|:+0#af5f00255&| +0#0000000&@42
+@2|%+0#0000e05&| |C|o|m|e|n|t| |w|i|t|h| |p|e|r|i|o|d|.| +0#0000000&@51
+@2|{+0#e000e06&|t+0#e000002&|o|k|e|n|,+0#0000000&|{+0#e000e06&|f+0#e000002&|l|o|a|t|,+0#0000000&|T+0#00e0e07&|o|k|e|n|L|i|n|e|,+0#0000000&|l+0#00e0e07&|i|s|t|_|t|o|_|f|l|o|a|t|(+0#0000000&|T+0#00e0e07&|o|k|e|n|C|h|a|r|s|)+0#0000000&|}+0#e000e06&@1|.+0#0000000&| @20
+|{+0#e000e06&|A|}| +0#0000000&|:+0#af5f00255&| +0#0000000&@1|E+0#00e0e07&|r|l|a|n|g|C|o|d|e|.+0#0000000&| |%+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@46
+|{+0#e000e06&|W|S|}| +0#0000000&|:+0#af5f00255&| +0#0000000&|E+0#00e0e07&|r|l|a|n|g|C|o|d|e|.+0#0000000&| @56
+@57|1|,|1| @10|T|o|p|
diff --git a/runtime/syntax/testdir/dumps/leex_01.dump b/runtime/syntax/testdir/dumps/leex_01.dump
new file mode 100644
index 000000000..3fd3d7229
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/leex_01.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@1|{+0#e000e06&|t+0#e000002&|o|k|e|n|,+0#0000000&|{+0#e000e06&|i+0#e000002&|n|t|e|g|e|r|,+0#0000000&|T+0#00e0e07&|o|k|e|n|L|i|n|e|,+0#0000000&|l+0#00e0e07&|i|s|t|_|t|o|_|i|n|t|e|g|e|r|(+0#0000000&|T+0#00e0e07&|o|k|e|n|C|h|a|r|s|)+0#0000000&|}+0#e000e06&@1|.+0#0000000&| @16
+|{+0#e000e06&|D|}|++0#af5f00255&|\+0#e000e06&|.|{|D|}|++0#af5f00255&|(+0#e000e06&@1|E+0#0000000&||+0#af5f00255&|e+0#0000000&|)+0#e000e06&|(|\|+||+0#af5f00255&|\+0#e000e06&|-|)|?+0#af5f00255&|{+0#e000e06&|D|}|++0#af5f00255&|)+0#e000e06&|?+0#af5f00255&| +0#0000000&|:+0#af5f00255&| +0#0000000&@42
+@2|%+0#0000e05&| |C|o|m|e|n|t| |w|i|t|h| |p|e|r|i|o|d|.| +0#0000000&@51
+@2|{+0#e000e06&|t+0#e000002&|o|k|e|n|,+0#0000000&|{+0#e000e06&|f+0#e000002&|l|o|a|t|,+0#0000000&|T+0#00e0e07&|o|k|e|n|L|i|n|e|,+0#0000000&|l+0#00e0e07&|i|s|t|_|t|o|_|f|l|o|a|t|(+0#0000000&|T+0#00e0e07&|o|k|e|n|C|h|a|r|s|)+0#0000000&|}+0#e000e06&@1|.+0#0000000&| @20
+|{+0#e000e06&|A|}| +0#0000000&|:+0#af5f00255&| +0#0000000&@1|E+0#00e0e07&|r|l|a|n|g|C|o|d|e|.+0#0000000&| |%+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@46
+>{+0#e000e06&|W|S|}| +0#0000000&|:+0#af5f00255&| +0#0000000&|E+0#00e0e07&|r|l|a|n|g|C|o|d|e|.+0#0000000&| @56
+|:|=| |:+0#af5f00255&|{+0#e000e06&|t+0#e000002&|o|k|e|n|,+0#0000000&|{+0#e000e06&|'+0#e000002&|:|=|'|,+0#0000000&|T+0#00e0e07&|o|k|e|n|L|i|n|e|}+0#e000e06&@1|.+0#0000000&| @45
+@75
+|E+0#e000e06&|r|l|a|n|g| |c|o|d|e|.| +0#0000000&@62
+@75
+|-+0#af5f00255&|e|x|p|o|r|t|(+0#0000000&|[+0#e000e06&|r+0#0000000&|e|s|e|r|v|e|d|_|w|o|r|d|/+0#af5f00255&|1+0#e000002&|]+0#e000e06&|)+0#0000000&|.| @47
+@75
+|%+0#0000e05&@1| |r|e|s|e|r|v|e|d|_|w|o|r|d|(|A|t|o|m|)| |-|>| |B|o@1|l| +0#0000000&@44
+|r|e|s|e|r|v|e|d|_|w|o|r|d|(|'+0#e000002&|r|e|s|e|r|v|e|d|'|)+0#0000000&| |-+0#af5f00255&|>| +0#0000000&|t+0#e000002&|r|u|e|;+0#0000000&| @40
+|r|e|s|e|r|v|e|d|_|w|o|r|d|(|_+0#00e0e07&|)+0#0000000&| |-+0#af5f00255&|>| +0#0000000&|f+0#e000002&|a|l|s|e|.+0#0000000&| @48
+|~+0#4040ff13&| @73
+|~| @73
+|~| @73
+|~| @73
+| +0#0000000&@56|1|9|,|1| @9|B|o|t|
diff --git a/runtime/syntax/testdir/input/leex.xrl b/runtime/syntax/testdir/input/leex.xrl
new file mode 100644
index 000000000..c901b8b87
--- /dev/null
+++ b/runtime/syntax/testdir/input/leex.xrl
@@ -0,0 +1,28 @@
+% Header comment
+%% Header comment
+%%% Header comment
+
+Definitions.
+floats = (\+|-)?[0-9]+\.[0-9]+((E|e)(\+|-)?[0-9]+)?
+D = [0-9]
+A = ({D}|_|@)
+WS = ([
+
+Rules.
+{D}+ :
+ % Comment
+ {token,{integer,TokenLine,list_to_integer(TokenChars)}}.
+{D}+\.{D}+((E|e)(\+|\-)?{D}+)? :
+ % Coment with period.
+ {token,{float,TokenLine,list_to_float(TokenChars)}}.
+{A} : ErlangCode. % comment
+{WS} : ErlangCode.
+:= :{token,{':=',TokenLine}}.
+
+Erlang code.
+
+-export([reserved_word/1]).
+
+%% reserved_word(Atom) -> Bool
+reserved_word('reserved') -> true;
+reserved_word(_) -> false.
diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim
index 0fef5ffe9..84aa338e5 100644
--- a/src/testdir/test_filetype.vim
+++ b/src/testdir/test_filetype.vim
@@ -440,6 +440,7 @@ def s:GetFilenameChecks(): dict<list<string>>
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++'],
diff --git a/src/version.c b/src/version.c
index 7a796a89c..8202641a5 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1936,
/**/
1935,
/**/
Reply all
Reply to author
Forward
0 new messages