diff options
Diffstat (limited to '')
-rw-r--r-- | runtime/syntax/generator/gen_syntax_vim.vim | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/runtime/syntax/generator/gen_syntax_vim.vim b/runtime/syntax/generator/gen_syntax_vim.vim new file mode 100644 index 0000000..b4feeed --- /dev/null +++ b/runtime/syntax/generator/gen_syntax_vim.vim @@ -0,0 +1,704 @@ +" Vim syntax file generator +" Language: Vim script +" Maintainer: Hirohito Higashi (h_east) +" URL: https://github.com/vim-jp/syntax-vim-ex +" Last Change: 2024 Mar 14 +" Version: 2.0.6 + +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 + " 99: (Exclude registration of "syn keyword") + 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 =~# 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 '/^}\s*event_names\[\]\s*=\s*$/+1;/^};/-1yank' + %delete _ + + put + g!/^\s*{\s*"\w\+"\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 '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) + " 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 |