diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 466370aa0d..5010b2e4e0 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -136,6 +136,7 @@ what kind of file it is. This doesn't always work. A number of global variables can be used to overrule the filetype used for certain extensions: file name variable ~ + `*.al` g:filetype_al `*.app` g:filetype_app `*.as` g:filetype_as `*.asa` g:filetype_asa |ft-aspperl-syntax| diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 727c2b0601..7ed04bead6 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -245,6 +245,7 @@ local extension = { adb = 'ada', tdf = 'ahdl', aidl = 'aidl', + al = detect.al, a68 = 'algol68', aml = 'aml', run = 'ampl', @@ -1006,7 +1007,6 @@ local extension = { plx = 'perl', prisma = 'prisma', psgi = 'perl', - al = 'perl', ctp = 'php', php = 'php', phpt = 'php', diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 6bd14be841..e02db364a4 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -31,6 +31,37 @@ local matchregex = vim.filetype._matchregex -- luacheck: push no unused args -- luacheck: push ignore 122 +-- AL (Microsoft Dynamics 365 Business Central) or Perl AutoLoader +--- @type vim.filetype.mapfn +function M.al(_, bufnr) + if vim.g.filetype_al then + return vim.g.filetype_al + end + -- AL sources declare an object as " " at the start of a + -- line, optionally preceded by namespace and using declarations. Perl + -- AutoLoader chunks match neither. Matching a bare keyword anywhere would + -- be wrong: table, page and report are ordinary English words, so the object + -- name must follow. The match is case sensitive because AL tooling emits + -- lowercase keywords, while prose in Perl comments is usually capitalised. + for _, line in ipairs(getlines(bufnr, 1, 200)) do + if + matchregex( + line, + [[^\s*\%(codeunit\|page\|pageextension\|pagecustomization\|table\|tableextension\|]] + .. [[report\|reportextension\|xmlport\|query\|enum\|enumextension\|profile\|profileextension\|]] + .. [[controladdin\|interface\|permissionset\|permissionsetextension\|entitlement\)\>\s\+\%(\d\|"\|\u\)]] + ) + or findany( + line, + { '^%s*dotnet%s*$', '^%s*namespace%s+[%w._]+%s*;', '^%s*using%s+[%w._]+%s*;' } + ) + then + return 'al' + end + end + return 'perl' +end + -- Erlang Application Resource Files (*.app.src is matched by extension) -- See: https://erlang.org/doc/system/applications --- @type vim.filetype.mapfn diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index ee52075079..c052cb214c 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -634,7 +634,7 @@ func s:GetFilenameChecks() abort \ 'pcmk': ['file.pcmk'], \ 'pdf': ['file.pdf'], \ 'pem': ['file.pem', 'file.cer', 'file.crt', 'file.csr'], - \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc', '.latexmkrc', 'latexmkrc'], + \ 'perl': ['file.plx', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc', '.latexmkrc', 'latexmkrc'], \ 'pf': ['pf.conf'], \ 'pfmain': ['main.cf', 'main.cf.proto'], \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp', 'file.phpt', 'file.theme'], @@ -1264,6 +1264,57 @@ endfunc " Keep sorted. """"""""""""""""""""""""""""""""""""""""""""""""" +func Test_al_file() + filetype on + + " AL object declaration + call writefile(['codeunit 50100 "My Codeunit"', '{', '}'], 'Xfile.al', 'D') + split Xfile.al + call assert_equal('al', &filetype) + bwipe! + + " AL namespace and using declarations before the object + call writefile(['namespace Microsoft.Sales;', '', 'using Microsoft.Foundation;'], 'Xfile.al') + split Xfile.al + call assert_equal('al', &filetype) + bwipe! + + " Perl AutoLoader chunk + call writefile(['# NOTE: Derived from blib/lib/Net/SSLeay.pm.', 'package Net::SSLeay;', 'sub do_https {'], 'Xfile.al') + split Xfile.al + call assert_equal('perl', &filetype) + bwipe! + + " AL DotNet alias object, which is a bare keyword on its own line + call writefile(['dotnet', '{', ' assembly("mscorlib")', '}'], 'Xfile.al') + split Xfile.al + call assert_equal('al', &filetype) + bwipe! + + " Perl code containing AL object kinds as ordinary words + call writefile(['sub value {', ' my $table = shift;', ' # report page interface', '}'], 'Xfile.al') + split Xfile.al + call assert_equal('perl', &filetype) + bwipe! + + " Perl documentation containing AL object kinds at the start of a line + call writefile(['package Foo;', '=pod', 'Table of contents', 'using the -x option', 'table of contents follows'], 'Xfile.al') + split Xfile.al + call assert_equal('perl', &filetype) + bwipe! + + " Test dist#ft#FTal() + + let g:filetype_al = 'perl' + call writefile(['codeunit 50100 "My Codeunit"'], 'Xfile.al') + split Xfile.al + call assert_equal('perl', &filetype) + bwipe! + unlet g:filetype_al + + filetype off +endfunc + " Since dist#ft#FTm4() looks around for configure.ac " the test needs to isolate itself in a fresh temporary project tree, " so that no configure.ac from another test (or from the repo root)