1
0
forked from aniani/vim
vim/runtime/syntax/generator/gen_syntax_vim.vim
Doug Kearns a44ced5763
runtime(vim): Update syntax generator, autocmd event list parsing
Track changes to the autocommand event list introduced in commit
78d742a (Patch 9.1.0256).

closes: #14430

Signed-off-by: Doug Kearns <dougkearns@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-04-07 17:49:57 +02:00

711 lines
18 KiB
VimL

" Vim syntax file generator
" Language: Vim script
" Maintainer: Hirohito Higashi (h_east)
" URL: https://github.com/vim-jp/syntax-vim-ex
" Last Change: 2024 Apr 07
" Version: 2.1.0
let s:keepcpo= &cpo
set cpo&vim
language C
function! s:parse_vim_option(opt, missing_opt, term_out_code)
try
let file_name = $VIM_SRCDIR . '/optiondefs.h'
let item = {}
new
exec 'read ' . file_name
norm! gg
exec '/^.*\s*options\[\]\s*=\s*$/+1;/^\s*#\s*define\s*p_term(/-1yank a'
exec '/^#define\s\+p_term(/+1;/^};$/-1yank b'
%delete _
put a
" workaround for 'shortname'
g/^#\s*ifdef\s*SHORT_FNAME\>/j
g/^#/d
g/^\s*{\s*"\w\+"\%(\s*,\s*[^,]*\)\{2}[^,]$/j
g/^\s*{\s*"\w\+"\s*,.*$/j
g!/^\s*{\s*"\w\+"\s*,.*$/d
for line in getline(1, line('$'))
let list = matchlist(line, '^\s*{\s*"\(\w\+\)"\s*,\s*\%("\(\w\+\)"\|NULL\)\s*,\s*\%([^,]*\(P_BOOL\)[^,]*\|[^,]*\)\s*,\s*\([^,]*NULL\)\?.*')
let item.name = list[1]
let item.short_name = list[2]
let item.is_bool = empty(list[3]) ? 0 : 1
if empty(list[4])
call add(a:opt, copy(item))
else
call add(a:missing_opt, copy(item))
endif
endfor
if empty(a:opt)
throw 'opt is empty'
endif
if empty(a:missing_opt)
throw 'missing_opt is empty'
endif
%delete _
put b
g!/^\s*p_term(\s*"\w\+"\s*,.*$/d
for line in getline(1, line('$'))
let list = matchlist(line, '^\s*p_term(\s*"\(\w\+\)"\s*,')
let item.name = list[1]
call add(a:term_out_code, copy(item))
endfor
quit!
if empty(a:term_out_code)
throw 'term_out_code is empty'
endif
catch /.*/
call s:err_gen('')
throw 'exit'
endtry
endfunc
function! s:append_syn_vimopt(lnum, str_info, opt_list, prefix, bool_only)
let ret_lnum = a:lnum
let str = a:str_info.start
for o in a:opt_list
if !a:bool_only || o.is_bool
if !empty(o.short_name)
let str .= ' ' . a:prefix . o.short_name
endif
let str .= ' ' . a:prefix . o.name
if len(str) > s:line_break_len
if !empty(a:str_info.end)
let str .= ' ' . a:str_info.end
endif
call append(ret_lnum, str)
let str = a:str_info.start
let ret_lnum += 1
endif
endif
endfor
if str !=# a:str_info.start
if !empty(a:str_info.end)
let str .= ' ' . a:str_info.end
endif
call append(ret_lnum, str)
let ret_lnum += 1
endif
return ret_lnum
endfunc
" ------------------------------------------------------------------------------
function! s:parse_vim_command(cmd)
try
let file_name = $VIM_SRCDIR . '/ex_cmds.h'
let item = {}
new
exec 'read ' . file_name
norm! gg
exec '/^}\?\s*cmdnames\[\]\s*=\s*$/+1;/^};/-1yank'
%delete _
put
g!/^EXCMD(/d
let lcmd = {}
for key in range(char2nr('a'), char2nr('z'))
let lcmd[nr2char(key)] = []
endfor
let lcmd['~'] = []
for line in getline(1, line('$'))
let list = matchlist(line, '^EXCMD(\w\+\s*,\s*"\(\a\w*\)"\s*,')
if !empty(list)
" Small ascii character or other.
let key = (list[1][:0] =~# '\l') ? list[1][:0] : '~'
call add(lcmd[key], list[1])
endif
endfor
quit!
for key in sort(keys(lcmd))
for my in range(len(lcmd[key]))
let omit_idx = 0
if my > 0
let omit_idx = (key =~# '\l') ? 1 : 0
for idx in range(1, strlen(lcmd[key][my]))
let spec=0
if lcmd[key][my] ==# 'ex'
let spec=1
echo "cmd name:" lcmd[key][my]
endif
let matched = 0
for pre in range(my - 1, 0, -1)
if spec
echo "pre:" pre ", my:" my
endif
if pre == my
if spec
echo "continue"
endif
continue
endif
" for weird abbreviations for delete. (See :help :d)
" And k{char} is used as mark. (See :help :k)
if lcmd[key][my][:idx] ==# lcmd[key][pre][:idx] ||
\ (key ==# 'd' &&
\ lcmd[key][my][:idx] =~# '^d\%[elete][lp]$')
\ || (key ==# 'k' &&
\ lcmd[key][my][:idx] =~# '^k[a-zA-Z]$')
let matched = 1
let omit_idx = idx + 1
if spec
echo "match. break. omit_idx:" omit_idx
endif
break
endif
endfor
if !matched
if spec
echo "not match. break"
endif
break
endif
endfor
endif
let item.name = lcmd[key][my]
let item.type = s:get_vim_command_type(item.name)
if omit_idx + 1 < strlen(item.name)
let item.omit_idx = omit_idx
let item.syn_str = item.name[:omit_idx] . '[' .
\ item.name[omit_idx+1:] . ']'
else
let item.omit_idx = -1
let item.syn_str = item.name
endif
call add(a:cmd, copy(item))
endfor
endfor
" Check exists in the help. (Usually it does not check...)
let doc_dir = './vim/runtime/doc'
if 0
for vimcmd in a:cmd
let find_ptn = '^|:' . vimcmd.name . '|\s\+'
exec "silent! vimgrep /" . find_ptn . "/gj " . doc_dir . "/index.txt"
let li = getqflist()
if empty(li)
call s:err_sanity(printf('Ex-cmd `:%s` is not found in doc/index.txt.', vimcmd.name))
elseif len(li) > 1
call s:err_sanity(printf('Ex-cmd `:%s` is duplicated in doc/index.txt.', vimcmd.name))
else
let doc_syn_str = substitute(li[0].text, find_ptn . '\(\S\+\)\s\+.*', '\1', '')
if doc_syn_str ==# vimcmd.syn_str
call s:err_sanity(printf('Ex-cmd `%s` short name differ in doc/index.txt. code: `%s`, document: `%s`', vimcmd.name, vimcmd.syn_str, doc_syn_str))
endif
endif
if 1
for i in range(2)
if i || vimcmd.omit_idx >= 0
if !i
let base_ptn = vimcmd.name[:vimcmd.omit_idx]
else
let base_ptn = vimcmd.name
endif
let find_ptn = '\*:' . base_ptn . '\*'
exec "silent! vimgrep /" . find_ptn . "/gj " . doc_dir . "/*.txt"
let li = getqflist()
if empty(li)
call s:err_sanity(printf('Ex-cmd `:%s`%s is not found in the help tag.', base_ptn, !i ? ' (short name of `:' . vimcmd.name . '`)' : ''))
elseif len(li) > 1
call s:err_sanity(printf('Ex-cmd `:%s`%s is duplicated in the help tag.', base_ptn, !i ? ' (short name of `:' . vimcmd.name . '`)' : ''))
endif
endif
endfor
endif
endfor
endif
" Add weird abbreviations for delete. (See :help :d)
for i in ['l', 'p']
let str = 'delete'
let item.name = str . i
let item.type = s:get_vim_command_type(item.name)
let item.omit_idx = -1
for x in range(strlen(str))
let item.syn_str = str[:x] . i
if item.syn_str !=# "del"
call add(a:cmd, copy(item))
endif
endfor
endfor
" Required for original behavior
let item.name = 'a' " append
let item.type = 0
let item.omit_idx = -1
let item.syn_str = item.name
call add(a:cmd, copy(item))
let item.name = 'i' " insert
let item.syn_str = item.name
call add(a:cmd, copy(item))
if empty(a:cmd)
throw 'cmd is empty'
endif
catch /.*/
call s:err_gen('')
throw 'exit'
endtry
endfunc
function! s:get_vim_command_type(cmd_name)
" Return value:
" 0: normal
" 1: (Reserved)
" 2: abbrev (without un)
" 3: menu
" 4: map
" 5: mapclear
" 6: unmap
" 7: abclear
" 99: (Exclude registration of "syn keyword")
let ab_prefix = '^[ci]\?'
let menu_prefix = '^\%([acinostvx]\?\|tl\)'
let map_prefix = '^[acilnostvx]\?'
let exclude_list = [
\ 'map', 'mapclear',
\ 'substitute', 'smagic', 'snomagic',
\ 'setlocal', 'setglobal', 'set', 'var',
\ 'autocmd', 'augroup', 'doautocmd', 'doautoall',
\ 'echo', 'echoconsole', 'echoerr', 'echohl', 'echomsg', 'echon', 'echowindow',
\ 'execute',
\ 'function', 'endfunction', 'def', 'enddef',
\ 'behave', 'augroup', 'normal', 'syntax',
\ 'append', 'insert',
\ 'Next', 'Print', 'X',
\ 'new', 'popup',
\ ]
" Required for original behavior
" \ 'global', 'vglobal'
if index(exclude_list, a:cmd_name) != -1
let ret = 99
elseif a:cmd_name =~# '^\%(\%(un\)\?abbreviate\|noreabbrev\|\l\%(nore\|un\)\?abbrev\)$'
let ret = 2
elseif a:cmd_name =~# ab_prefix . 'abclear$'
let ret = 7
elseif a:cmd_name =~# menu_prefix . '\%(nore\|un\)\?menu$'
let ret = 3
elseif a:cmd_name =~# map_prefix . '\%(nore\)\?map$'
let ret = 4
elseif a:cmd_name =~# map_prefix . 'mapclear$'
let ret = 5
elseif a:cmd_name =~# map_prefix . 'unmap$'
let ret = 6
else
let ret = 0
endif
return ret
endfunc
function! s:append_syn_vimcmd(lnum, str_info, cmd_list, type)
let ret_lnum = a:lnum
let str = a:str_info.start
for o in a:cmd_list
if o.type == a:type
let str .= ' ' . o.syn_str
if len(str) > s:line_break_len
if !empty(a:str_info.end)
let str .= ' ' . a:str_info.end
endif
call append(ret_lnum, str)
let str = a:str_info.start
let ret_lnum += 1
endif
endif
endfor
if str !=# a:str_info.start
if !empty(a:str_info.end)
let str .= ' ' . a:str_info.end
endif
call append(ret_lnum, str)
let ret_lnum += 1
endif
return ret_lnum
endfunc
" ------------------------------------------------------------------------------
function! s:parse_vim_event(li)
try
let file_name = $VIM_SRCDIR . '/autocmd.c'
let item = {}
new
exec 'read ' . file_name
norm! gg
exec '/^static keyvalue_T event_tab\[] = {$/+1;/^};$/-1yank'
%delete _
put
g!/^\s*KEYVALUE_ENTRY(/d
for line in getline(1, line('$'))
let list = matchlist(line, '^\s*KEYVALUE_ENTRY(EVENT_\w\+,\s*"\(\w\+\)"')
let item.name = list[1]
call add(a:li, copy(item))
endfor
quit!
if empty(a:li)
throw 'event is empty'
endif
catch /.*/
call s:err_gen('')
throw 'exit'
endtry
endfunc
" ------------------------------------------------------------------------------
function! s:parse_vim_function(li)
try
let file_name = $VIM_SRCDIR . '/evalfunc.c'
let item = {}
new
exec 'read ' . file_name
norm! gg
exec '/^static\s\+funcentry_T\s\+global_functions\[\]\s*=\s*$/+1;/^};/-1yank'
%delete _
put
g!/^\s*{\s*"\w\+"\s*,.*$/d
g/^\s*{\s*"test"\s*,.*$/d
g@//\s*obsolete@d
g@/\*\s*obsolete\s*\*/@d
for line in getline(1, line('$'))
let list = matchlist(line, '^\s*{\s*"\(\w\+\)"\s*,')
let item.name = list[1]
call add(a:li, copy(item))
endfor
quit!
if empty(a:li)
throw 'function is empty'
endif
catch /.*/
call s:err_gen('')
throw 'exit'
endtry
endfunc
" ------------------------------------------------------------------------------
function! s:parse_vim_hlgroup(li)
try
let file_name = $VIM_SRCDIR . '/highlight.c'
let item = {}
new
exec 'read ' . file_name
call cursor(1, 1)
exec '/^static\s\+char\s\+\*(highlight_init_both\[\])\s*=\%(\s*{\)\?$/+1;/^\s*};/-1yank a'
exec '/^static\s\+char\s\+\*(highlight_init_light\[\])\s*=\%(\s*{\)\?$/+1;/^\s*};/-1yank b'
exec '/^set_normal_colors(\%(void\)\?)$/+1;/^}$/-1yank d'
%delete _
put a
for line in getline(1, line('$'))
let list = matchlist(line, '^\s*\%(CENT(\)\?"\%(default\s\+link\s\+\)\?\(\a\+\).*",.*')
if !empty(list)
let item.name = list[1]
let item.type = 'both'
call add(a:li, copy(item))
endif
endfor
%delete _
put b
for line in getline(1, line('$'))
let list = matchlist(line, '^\s*\%(CENT(\)\?"\%(default\s\+link\s\+\)\?\(\a\+\).*",.*')
if !empty(list)
let item.name = list[1]
let item.type = 'light'
call add(a:li, copy(item))
endif
endfor
%delete _
put d
for line in getline(1, line('$'))
let list = matchlist(line, '^\s*if\s*(set_group_colors(.*"\(\a\+\)",')
if !empty(list) && list[1] !=# 'Normal'
let item.name = list[1]
let item.type = 'gui'
call add(a:li, copy(item))
endif
endfor
let item.name = 'CursorIM'
let item.type = 'gui'
call add(a:li, copy(item))
" The following highlight groups cannot be extracted from highlight.c
" (TODO: extract from HIGHLIGHT_INIT ?)
let item.name = 'LineNrAbove'
let item.type = 'both'
call add(a:li, copy(item))
let item.name = 'LineNrBelow'
let item.type = 'both'
call add(a:li, copy(item))
quit!
if empty(a:li)
throw 'hlgroup is empty'
endif
catch /.*/
call s:err_gen('')
throw 'exit'
endtry
endfunc
" ------------------------------------------------------------------------------
function! s:parse_vim_complete_name(li)
try
let file_name = $VIM_SRCDIR . '/usercmd.c'
let item = {}
new
exec 'read ' . file_name
norm! gg
exec '/^}\s*command_complete\[\]\s*=\s*$/+1;/^};/-1yank'
%delete _
put
g!/^\s*{.*"\w\+"\s*}\s*,.*$/d
g/"custom\(list\)\?"/d
for line in getline(1, line('$'))
let list = matchlist(line, '^\s*{.*"\(\w\+\)"\s*}\s*,')
let item.name = list[1]
call add(a:li, copy(item))
endfor
quit!
if empty(a:li)
throw 'complete_name is empty'
endif
catch /.*/
call s:err_gen('')
throw 'exit'
endtry
endfunc
" ------------------------------------------------------------------------------
function! s:append_syn_any(lnum, str_info, li)
let ret_lnum = a:lnum
let str = a:str_info.start
for o in a:li
let str .= ' ' . o.name
if len(str) > s:line_break_len
if !empty(a:str_info.end)
let str .= ' ' . a:str_info.end
endif
call append(ret_lnum, str)
let str = a:str_info.start
let ret_lnum += 1
endif
endfor
if str !=# a:str_info.start
if !empty(a:str_info.end)
let str .= ' ' . a:str_info.end
endif
call append(ret_lnum, str)
let ret_lnum += 1
endif
return ret_lnum
endfunc
function! s:update_syntax_vim_file(vim_info)
try
function! s:search_and_check(kword, base_fname, str_info)
let a:str_info.start = ''
let a:str_info.end = ''
let pattern = '^" GEN_SYN_VIM: ' . a:kword . '\s*,'
let lnum = search(pattern)
if lnum == 0
throw 'Search pattern ''' . pattern . ''' not found in ' .
\ a:base_fname
endif
let li = matchlist(getline(lnum), pattern . '\s*START_STR\s*=\s*''\(.\{-}\)''\s*,\s*END_STR\s*=\s*''\(.\{-}\)''')
if empty(li)
throw 'Bad str_info line:' . getline(lnum)
endif
let a:str_info.start = li[1]
let a:str_info.end = li[2]
return lnum
endfunc
let target_fname = 'vim.vim.rc'
let base_fname = 'vim.vim.base'
let str_info = {}
let str_info.start = ''
let str_info.end = ''
new
exec 'edit ' . target_fname
%d _
exec 'read ' . base_fname
1delete _
call cursor(1, 1)
" vimCommand
let li = a:vim_info.cmd
" vimCommand - normal
let lnum = s:search_and_check('vimCommand normal', base_fname, str_info)
let lnum = s:append_syn_vimcmd(lnum, str_info, li, 0)
" vimOption
let kword = 'vimOption'
let li = a:vim_info.opt
" vimOption - normal
let lnum = s:search_and_check(kword . ' normal', base_fname, str_info)
let lnum = s:append_syn_vimopt(lnum, str_info, li, '', 0)
" vimOption - turn-off
let lnum = s:search_and_check(kword . ' turn-off', base_fname, str_info)
let lnum = s:append_syn_vimopt(lnum, str_info, li, 'no', 1)
" vimOption - invertible
let lnum = s:search_and_check(kword . ' invertible', base_fname, str_info)
let lnum = s:append_syn_vimopt(lnum, str_info, li, 'inv', 1)
" vimOption - term output code
let li = a:vim_info.term_out_code
let lnum = s:search_and_check(kword . ' term output code', base_fname, str_info)
let lnum = s:append_syn_any(lnum, str_info, li)
" Missing vimOption
let li = a:vim_info.missing_opt
let lnum = s:search_and_check('Missing vimOption', base_fname, str_info)
let lnum = s:append_syn_vimopt(lnum, str_info, li, '', 0)
let lnum = s:append_syn_vimopt(lnum, str_info, li, 'no', 1)
let lnum = s:append_syn_vimopt(lnum, str_info, li, 'inv', 1)
" vimAutoEvent
let li = a:vim_info.event
let lnum = s:search_and_check('vimAutoEvent', base_fname, str_info)
let lnum = s:append_syn_any(lnum, str_info, li)
" vimHLGroup
let li = a:vim_info.hlgroup
let lnum = s:search_and_check('vimHLGroup', base_fname, str_info)
let lnum = s:append_syn_any(lnum, str_info, li)
" vimFuncName
let li = a:vim_info.func
let lnum = s:search_and_check('vimFuncName', base_fname, str_info)
let lnum = s:append_syn_any(lnum, str_info, li)
" vimUserAttrbCmplt
let li = a:vim_info.compl_name
let lnum = s:search_and_check('vimUserAttrbCmplt', base_fname, str_info)
let lnum = s:append_syn_any(lnum, str_info, li)
" vimCommand - abbrev
let kword = 'vimCommand'
let li = a:vim_info.cmd
let lnum = s:search_and_check(kword . ' abbrev', base_fname, str_info)
let lnum = s:append_syn_vimcmd(lnum, str_info, li, 2)
let lnum = s:search_and_check(kword . ' abclear', base_fname, str_info)
let lnum = s:append_syn_vimcmd(lnum, str_info, li, 7)
" vimCommand - map
let lnum = s:search_and_check(kword . ' map', base_fname, str_info)
let lnum = s:append_syn_vimcmd(lnum, str_info, li, 4)
let lnum = s:search_and_check(kword . ' mapclear', base_fname, str_info)
let lnum = s:append_syn_vimcmd(lnum, str_info, li, 5)
let lnum = s:search_and_check(kword . ' unmap', base_fname, str_info)
let lnum = s:append_syn_vimcmd(lnum, str_info, li, 6)
" vimCommand - menu
let lnum = s:search_and_check(kword . ' menu', base_fname, str_info)
let lnum = s:append_syn_vimcmd(lnum, str_info, li, 3)
update
quit!
catch /.*/
call s:err_gen('')
throw 'exit'
endtry
endfunc
" ------------------------------------------------------------------------------
function! s:err_gen(arg)
call s:write_error(a:arg, 'generator.err')
endfunc
function! s:err_sanity(arg)
call s:write_error(a:arg, 'sanity_check.err')
endfunc
function! s:write_error(arg, fname)
let li = []
if !empty(v:throwpoint)
call add(li, v:throwpoint)
endif
if !empty(v:exception)
call add(li, v:exception)
endif
if type(a:arg) == type([])
call extend(li, a:arg)
elseif type(a:arg) == type("")
if !empty(a:arg)
call add(li, a:arg)
endif
endif
if !empty(li)
call writefile(li, a:fname, 'a')
else
call writefile(['UNKNOWN'], a:fname, 'a')
endif
endfunc
" ------------------------------------------------------------------------------
try
let s:line_break_len = 768
let s:vim_info = {}
let s:vim_info.opt = []
let s:vim_info.missing_opt = []
let s:vim_info.term_out_code = []
let s:vim_info.cmd = []
let s:vim_info.event = []
let s:vim_info.func = []
let s:vim_info.hlgroup = []
let s:vim_info.compl_name = []
set lazyredraw
silent call s:parse_vim_option(s:vim_info.opt, s:vim_info.missing_opt,
\ s:vim_info.term_out_code)
silent call s:parse_vim_command(s:vim_info.cmd)
silent call s:parse_vim_event(s:vim_info.event)
silent call s:parse_vim_function(s:vim_info.func)
silent call s:parse_vim_hlgroup(s:vim_info.hlgroup)
silent call s:parse_vim_complete_name(s:vim_info.compl_name)
call s:update_syntax_vim_file(s:vim_info)
set nolazyredraw
finally
quitall!
endtry
" ---------------------------------------------------------------------
let &cpo = s:keepcpo
unlet s:keepcpo
" vim:ts=2 sw=2