1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-03 06:50:05 +08:00
SpaceVim/bundle/neco-syntax/autoload/necosyntax.vim
2020-06-13 14:06:35 +08:00

266 lines
6.6 KiB
VimL
Vendored

"=============================================================================
" FILE: necosyntax.vim
" AUTHOR: Shougo Matsushita <Shougo.Matsu at gmail.com>
" License: MIT license
"=============================================================================
let g:necosyntax#min_keyword_length =
\ get(g:, 'necosyntax#min_keyword_length', 4)
let g:necosyntax#max_syntax_lines =
\ get(g:, 'necosyntax#max_syntax_lines', 300)
function! necosyntax#initialize() abort
let s:syntax_list = {}
augroup necosyntax
autocmd!
autocmd Syntax * call s:make_cache()
augroup END
" Initialize check.
call s:make_cache()
endfunction
function! necosyntax#gather_candidates() abort
let filetype = &filetype
if filetype == ''
return []
endif
if !has_key(s:syntax_list, filetype)
call s:make_cache()
endif
let list = []
for ft in s:get_context_filetypes(filetype)
let list += get(s:syntax_list, ft, [])
endfor
return list
endfunction
function! s:make_cache() abort
let ft = &filetype
if ft == '' || ft ==# 'vim' || has_key(s:syntax_list, ft)
return
endif
" Make cache from syntax list.
let s:syntax_list[ft] = s:make_cache_from_syntax(ft)
endfunction
function! s:make_cache_from_syntax(filetype) abort
" Get current syntax list.
let syntax_list = s:redir('syntax list')
if syntax_list =~ '^E\d\+' || syntax_list =~ '^No Syntax items'
return []
endif
let lines = split(syntax_list, '\n')
if len(lines) > g:necosyntax#max_syntax_lines
" Too long.
return []
endif
let group_name = ''
let keyword_pattern = '\h\w*'
let keyword_list = []
for line in lines
if line =~ '^\h\w\+'
" Change syntax group name.
let group_name = matchstr(line, '^\S\+')
let line = substitute(line, '^\S\+\s*xxx', '', '')
endif
if line =~ 'Syntax items' || line =~ '^\s*links to' ||
\ line =~ '^\s*nextgroup='
" Next line.
continue
endif
let line = substitute(line,
\ 'contained\|skipwhite\|skipnl\|oneline', '', 'g')
let line = substitute(line,
\ '^\s*nextgroup=.*\ze\s', '', '')
if line =~ '^\s*match'
let line = s:substitute_candidate(
\ matchstr(line, '/\zs[^/]\+\ze/'))
elseif line =~ '^\s*start='
let line =
\s:substitute_candidate(
\ matchstr(line, 'start=/\zs[^/]\+\ze/')) . ' ' .
\s:substitute_candidate(
\ matchstr(line, 'end=/zs[^/]\+\ze/'))
endif
" Add keywords.
let match_num = 0
let match_str = matchstr(line, keyword_pattern, match_num)
while match_str != ''
" Ignore too short keyword.
if len(match_str) >= g:necosyntax#min_keyword_length
\&& match_str =~ '^[[:print:]]\+$'
call add(keyword_list, match_str)
endif
let match_num += len(match_str)
let match_str = matchstr(line, keyword_pattern, match_num)
endwhile
endfor
let keyword_list = s:uniq(keyword_list)
return keyword_list
endfunction
function! s:substitute_candidate(candidate) abort
let candidate = a:candidate
" Collection.
let candidate = substitute(candidate,
\'\\\@<!\[[^\]]*\]', ' ', 'g')
" Delete.
let candidate = substitute(candidate,
\'\\\@<!\%(\\[=?+]\|\\%[\|\\s\*\)', '', 'g')
" Space.
let candidate = substitute(candidate,
\'\\\@<!\%(\\[<>{}]\|[$^]\|\\z\?\a\)', ' ', 'g')
if candidate =~ '\\%\?('
let candidate = join(necosyntax#_split_pattern(
\ candidate, ''))
endif
" \
let candidate = substitute(candidate, '\\\\', '\\', 'g')
" *
let candidate = substitute(candidate, '\\\*', '*', 'g')
return candidate
endfunction
function! necosyntax#_split_pattern(keyword_pattern, prefix) abort
let original_pattern = a:keyword_pattern
let result_patterns = []
let analyzing_patterns = [ '' ]
let i = 0
let max = len(original_pattern)
while i < max
if match(original_pattern, '^\\%\?(', i) >= 0
" Grouping.
let end = s:match_pair(original_pattern, '\\%\?(', '\\)', i)
if end < 0
break
endif
let save_patterns = analyzing_patterns
let analyzing_patterns = []
for pattern in filter(save_patterns, "v:val !~ '.*\\s\\+.*\\s'")
let analyzing_patterns += necosyntax#_split_pattern(
\ original_pattern[matchend(original_pattern,
\ '^\\%\?(', i) : end-1],
\ pattern)
endfor
let i = end + 2
elseif match(original_pattern, '^\\|', i) >= 0
" Select.
let result_patterns += analyzing_patterns
let analyzing_patterns = [ '' ]
let original_pattern = original_pattern[i+2 :]
let max = len(original_pattern)
let i = 0
elseif original_pattern[i] == '\' && i+1 < max
call map(analyzing_patterns, 'v:val . original_pattern[i+1]')
" Escape.
let i += 2
else
call map(analyzing_patterns, 'v:val . original_pattern[i]')
let i += 1
endif
endwhile
let result_patterns += analyzing_patterns
return map(result_patterns, 'a:prefix . v:val')
endfunction
function! s:match_pair(string, start_pattern, end_pattern, start_cnt) abort
let end = -1
let start_pattern = '\%(' . a:start_pattern . '\)'
let end_pattern = '\%(' . a:end_pattern . '\)'
let i = a:start_cnt
let max = len(a:string)
let nest_level = 0
while i < max
let start = match(a:string, start_pattern, i)
let end = match(a:string, end_pattern, i)
if start >= 0 && (end < 0 || start < end)
let i = matchend(a:string, start_pattern, i)
let nest_level += 1
elseif end >= 0 && (start < 0 || end < start)
let nest_level -= 1
if nest_level == 0
return end
endif
let i = matchend(a:string, end_pattern, i)
else
break
endif
endwhile
if nest_level != 0
return -1
else
return end
endif
endfunction
function! s:uniq(list) abort
let dict = {}
for item in a:list
if !has_key(dict, item)
let dict[item] = item
endif
endfor
return values(dict)
endfunction
function! s:get_context_filetypes(filetype) abort
if !exists('s:exists_context_filetype')
try
call context_filetype#version()
let s:exists_context_filetype = 1
catch
let s:exists_context_filetype = 0
endtry
endif
return s:exists_context_filetype
\ && exists('*context_filetype#get_filetypes') ?
\ context_filetype#get_filetypes(a:filetype) : [a:filetype]
endfunction
function! s:redir(command) abort
if exists('*execute')
return execute(a:command)
endif
redir => r
execute 'silent!' a:command
redir END
return r
endfunction