vim-patch:9.1.1307: make syntax does not reliably detect different flavors (#33498)

Problem:  GNU extensions, such as `ifeq` and `wildcard` function, are
          highlighted in BSDmakefile
Solution: detect BSD, GNU, or Microsoft implementation according to
	  filename, user-defined global variables, or file contents

closes: vim/vim#17089

f35bd76b31

Co-authored-by: Eisuke Kawashima <e-kwsm@users.noreply.github.com>
Co-authored-by: Roland Hieber <rohieb@users.noreply.github.com>
This commit is contained in:
zeertzjq
2025-04-17 07:13:05 +08:00
committed by GitHub
parent 95e29dab70
commit e3e8dfe99c
6 changed files with 122 additions and 26 deletions

View File

@ -156,6 +156,8 @@ variables can be used to overrule the filetype used for certain extensions:
`*.inc` g:filetype_inc
`*.lsl` g:filetype_lsl
`*.m` g:filetype_m |ft-mathematica-syntax|
`*[mM]makefile,*.mk,*.mak,[mM]akefile*`
g:make_flavor |ft-make-syntax|
`*.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md`
g:filetype_md |ft-pandoc-syntax|
`*.mod` g:filetype_mod

View File

@ -1914,11 +1914,16 @@ Comments are also highlighted by default. You can turn this off by using: >
:let make_no_comments = 1
Microsoft Makefile handles variable expansion and comments differently
(backslashes are not used for escape). If you see any wrong highlights
because of this, you can try this: >
There are various Make implementations, which add extensions other than the
POSIX specification and thus are mutually incompatible. If the filename is
BSDmakefile or GNUmakefile, the corresponding implementation is automatically
determined; otherwise vim tries to detect it by the file contents. If you see
any wrong highlights because of this, you can enforce a flavor by setting one
of the following: >
:let make_microsoft = 1
:let g:make_flavor = 'bsd' " or
:let g:make_flavor = 'gnu' " or
:let g:make_flavor = 'microsoft'
MAPLE *maple.vim* *ft-maple-syntax*

View File

@ -2290,7 +2290,7 @@ local pattern = {
['^Containerfile%.'] = starsetf('dockerfile'),
['^Dockerfile%.'] = starsetf('dockerfile'),
['[mM]akefile$'] = detect.make,
['^[mM]akefile'] = starsetf('make'),
['^[mM]akefile'] = starsetf(detect.make),
['^[rR]akefile'] = starsetf('ruby'),
['^%.profile'] = detect.sh,
},

View File

@ -1021,16 +1021,46 @@ end
--- Check if it is a Microsoft Makefile
--- @type vim.filetype.mapfn
function M.make(_, bufnr)
vim.b.make_microsoft = nil
function M.make(path, bufnr)
vim.b.make_flavor = nil
-- 1. filename
local file_name = fn.fnamemodify(path, ':t')
if file_name == 'BSDmakefile' then
vim.b.make_flavor = 'bsd'
return 'make'
elseif file_name == 'GNUmakefile' then
vim.b.make_flavor = 'gnu'
return 'make'
end
-- 2. user's setting
if vim.g.make_flavor ~= nil then
vim.b.make_flavor = vim.g.make_flavor
return 'make'
elseif vim.g.make_microsoft ~= nil then
vim._truncated_echo_once(
"make_microsoft is deprecated; try g:make_flavor = 'microsoft' instead"
)
vim.b.make_flavor = 'microsoft'
return 'make'
end
-- 3. try to detect a flavor from file content
for _, line in ipairs(getlines(bufnr, 1, 1000)) do
if matchregex(line, [[\c^\s*!\s*\(ifn\=\(def\)\=\|include\|message\|error\)\>]]) then
vim.b.make_microsoft = 1
vim.b.make_flavor = 'microsoft'
break
elseif
matchregex(line, [[^ *ifn\=\(eq\|def\)\>]])
or findany(line, { '^ *[-s]?%s', '^ *%w+%s*[!?:+]=' })
matchregex(line, [[^\.\%(export\|error\|for\|if\%(n\=\%(def\|make\)\)\=\|info\|warning\)\>]])
then
vim.b.make_flavor = 'bsd'
break
elseif
matchregex(line, [[^ *\%(ifn\=\%(eq\|def\)\|define\|override\)\>]])
or line:find('%$[({][a-z-]+%s+%S+') -- a function call, e.g. $(shell pwd)
then
vim.b.make_flavor = 'gnu'
break
end
end

View File

@ -4,6 +4,7 @@
" Previous Maintainer: Claudio Fleiner <claudio@fleiner.com>
" URL: https://github.com/vim/vim/blob/master/runtime/syntax/make.vim
" Last Change: 2022 Nov 06
" 2025 Apr 15 by Vim project: rework Make flavor detection (#17089)
" quit when a syntax file was already loaded
if exists("b:current_syntax")
@ -13,6 +14,9 @@ endif
let s:cpo_save = &cpo
set cpo&vim
" enable GNU extension when b:make_flavor is not set—detection failed or Makefile is POSIX-compliant
let s:make_flavor = 'gnu'
" some special characters
syn match makeSpecial "^\s*[@+-]\+"
syn match makeNextLine "\\\n\s*"
@ -21,14 +25,16 @@ syn match makeNextLine "\\\n\s*"
syn region makeDefine start="^\s*define\s" end="^\s*endef\s*\(#.*\)\?$"
\ contains=makeStatement,makeIdent,makePreCondit,makeDefine
" Microsoft Makefile specials
syn case ignore
syn match makeInclude "^!\s*include\s.*$"
syn match makePreCondit "^!\s*\(cmdswitches\|error\|message\|include\|if\|ifdef\|ifndef\|else\|else\s*if\|else\s*ifdef\|else\s*ifndef\|endif\|undef\)\>"
syn case match
if get(b:, 'make_flavor', s:make_flavor) == 'microsoft'
" Microsoft Makefile specials
syn case ignore
syn match makeInclude "^!\s*include\s.*$"
syn match makePreCondit "^!\s*\(cmdswitches\|error\|message\|include\|if\|ifdef\|ifndef\|else\|else\s*if\|else\s*ifdef\|else\s*ifndef\|endif\|undef\)\>"
syn case match
endif
" identifiers
if exists("b:make_microsoft") || exists("make_microsoft")
if get(b:, 'make_flavor', s:make_flavor) == 'microsoft'
syn region makeIdent start="\$(" end=")" contains=makeStatement,makeIdent
syn region makeIdent start="\${" end="}" contains=makeStatement,makeIdent
else
@ -59,13 +65,31 @@ syn match makeTarget "^[~A-Za-z0-9_./$(){}%*@-][A-Za-z0-9_./\t $(){}%*
\ skipnl nextgroup=makeCommands,makeCommandError
syn region makeSpecTarget transparent matchgroup=makeSpecTarget
\ start="^\.\(SUFFIXES\|PHONY\|DEFAULT\|PRECIOUS\|IGNORE\|SILENT\|EXPORT_ALL_VARIABLES\|KEEP_STATE\|LIBPATTERNS\|NOTPARALLEL\|DELETE_ON_ERROR\|INTERMEDIATE\|POSIX\|SECONDARY\|ONESHELL\)\>\s*:\{1,2}[^:=]"rs=e-1
\ start="^\.\(SUFFIXES\|PHONY\|DEFAULT\|PRECIOUS\|IGNORE\|SILENT\|NOTPARALLEL\|POSIX\)\>\s*:\{1,2}[^:=]"rs=e-1
\ end="[^\\]$" keepend
\ contains=makeIdent,makeSpecTarget,makeNextLine,makeComment skipnl nextGroup=makeCommands
syn match makeSpecTarget "^\.\(SUFFIXES\|PHONY\|DEFAULT\|PRECIOUS\|IGNORE\|SILENT\|EXPORT_ALL_VARIABLES\|KEEP_STATE\|LIBPATTERNS\|NOTPARALLEL\|DELETE_ON_ERROR\|INTERMEDIATE\|POSIX\|SECONDARY\|ONESHELL\)\>\s*::\=\s*$"
syn match makeSpecTarget "^\.\(SUFFIXES\|PHONY\|DEFAULT\|PRECIOUS\|IGNORE\|SILENT\|NOTPARALLEL\|POSIX\)\>\s*::\=\s*$"
\ contains=makeIdent,makeComment
\ skipnl nextgroup=makeCommands,makeCommandError
if get(b:, 'make_flavor', s:make_flavor) == 'bsd'
syn region makeSpecTarget transparent matchgroup=makeSpecTarget
\ start="^\.DELETE_ON_ERROR\>\s*:\{1,2}[^:=]"rs=e-1
\ end="[^\\]$" keepend
\ contains=makeIdent,makeSpecTarget,makeNextLine,makeComment skipnl nextGroup=makeCommands
syn match makeSpecTarget "^\.DELETE_ON_ERROR\>\s*::\=\s*$"
\ contains=makeIdent,makeComment
\ skipnl nextgroup=makeCommands,makeCommandError
elseif get(b:, 'make_flavor', s:make_flavor) == 'gnu'
syn region makeSpecTarget transparent matchgroup=makeSpecTarget
\ start="^\.\(EXPORT_ALL_VARIABLES\|DELETE_ON_ERROR\|INTERMEDIATE\|KEEP_STATE\|LIBPATTERNS\|ONESHELL\|SECONDARY\)\>\s*:\{1,2}[^:=]"rs=e-1
\ end="[^\\]$" keepend
\ contains=makeIdent,makeSpecTarget,makeNextLine,makeComment skipnl nextGroup=makeCommands
syn match makeSpecTarget "^\.\(EXPORT_ALL_VARIABLES\|DELETE_ON_ERROR\|INTERMEDIATE\|KEEP_STATE\|LIBPATTERNS\|ONESHELL\|SECONDARY\)\>\s*::\=\s*$"
\ contains=makeIdent,makeComment
\ skipnl nextgroup=makeCommands,makeCommandError
endif
syn match makeCommandError "^\s\+\S.*" contained
syn region makeCommands contained start=";"hs=s+1 start="^\t"
\ end="^[^\t#]"me=e-1,re=e-1 end="^$"
@ -74,17 +98,19 @@ syn region makeCommands contained start=";"hs=s+1 start="^\t"
syn match makeCmdNextLine "\\\n."he=e-1 contained
" some directives
syn match makePreCondit "^ *\(ifn\=\(eq\|def\)\>\|else\(\s\+ifn\=\(eq\|def\)\)\=\>\|endif\>\)"
syn match makeInclude "^ *[-s]\=include\s.*$"
syn match makeStatement "^ *vpath"
syn match makeExport "^ *\(export\|unexport\)\>"
syn match makeOverride "^ *override\>"
" Statements / Functions (GNU make)
syn match makeStatement contained "(\(abspath\|addprefix\|addsuffix\|and\|basename\|call\|dir\|error\|eval\|file\|filter-out\|filter\|findstring\|firstword\|flavor\|foreach\|guile\|if\|info\|join\|lastword\|notdir\|or\|origin\|patsubst\|realpath\|shell\|sort\|strip\|subst\|suffix\|value\|warning\|wildcard\|word\|wordlist\|words\)\>"ms=s+1
if get(b:, 'make_flavor', s:make_flavor) == 'gnu'
" Statements / Functions (GNU make)
syn match makePreCondit "^ *\(ifn\=\(eq\|def\)\>\|else\(\s\+ifn\=\(eq\|def\)\)\=\>\|endif\>\)"
syn match makeStatement "^ *vpath\>"
syn match makeOverride "^ *override\>"
syn match makeStatement contained "[({]\(abspath\|addprefix\|addsuffix\|and\|basename\|call\|dir\|error\|eval\|file\|filter-out\|filter\|findstring\|firstword\|flavor\|foreach\|guile\|if\|info\|intcmp\|join\|lastword\|let\|notdir\|or\|origin\|patsubst\|realpath\|shell\|sort\|strip\|subst\|suffix\|value\|warning\|wildcard\|word\|wordlist\|words\)\>"ms=s+1
endif
" Comment
if !exists("make_no_comments")
if exists("b:make_microsoft") || exists("make_microsoft")
if get(b:, 'make_flavor', s:make_flavor) == 'microsoft'
syn match makeComment "#.*" contains=@Spell,makeTodo
else
syn region makeComment start="#" end="^$" end="[^\\]$" keepend contains=@Spell,makeTodo

View File

@ -2851,15 +2851,48 @@ endfunc
func Test_make_file()
filetype on
" BSD Makefile
call writefile([''], 'BSDmakefile', 'D')
split BSDmakefile
call assert_equal('bsd', get(b:, 'make_flavor', ''))
bwipe!
call writefile(['.ifmake all', '.endif'], 'XMakefile.mak', 'D')
split XMakefile.mak
call assert_equal('bsd', get(b:, 'make_flavor', ''))
bwipe!
" GNU Makefile
call writefile([''], 'GNUmakefile', 'D')
split GNUmakefile
call assert_equal('gnu', get(b:, 'make_flavor', ''))
bwipe!
call writefile(['ifeq ($(foo),foo)', 'endif'], 'XMakefile.mak', 'D')
split XMakefile.mak
call assert_equal('gnu', get(b:, 'make_flavor', ''))
bwipe!
call writefile(['define foo', 'endef'], 'XMakefile.mak', 'D')
split XMakefile.mak
call assert_equal('gnu', get(b:, 'make_flavor', ''))
bwipe!
call writefile(['vim := $(wildcard *.vim)'], 'XMakefile.mak', 'D')
split XMakefile.mak
call assert_equal('gnu', get(b:, 'make_flavor', ''))
bwipe!
" Microsoft Makefile
call writefile(['# Makefile for Windows', '!if "$(VIMDLL)" == "yes"'], 'XMakefile.mak', 'D')
split XMakefile.mak
call assert_equal(1, get(b:, 'make_microsoft', 0))
call assert_equal('microsoft', get(b:, 'make_flavor', ''))
bwipe!
" BSD or GNU
call writefile(['# get the list of tests', 'include testdir/Make_all.mak'], 'XMakefile.mak', 'D')
split XMakefile.mak
call assert_equal(0, get(b:, 'make_microsoft', 0))
call assert_notequal('microsoft', get(b:, 'make_flavor', ''))
bwipe!
filetype off