From 5a93f51abece038b34bdf297d7e4567fd0bcafa2 Mon Sep 17 00:00:00 2001 From: hrsh7th Date: Tue, 19 Nov 2019 13:26:24 +0900 Subject: [PATCH] Introduce floatwin/popupwin compatible layer --- autoload/lsp/ui/vim.vim | 23 +-- autoload/lsp/ui/vim/floatwin.vim | 183 ++++++++++++++++++++++++ autoload/lsp/ui/vim/floatwin/nvim.vim | 78 +++++++++++ autoload/lsp/ui/vim/floatwin/vim.vim | 83 +++++++++++ autoload/lsp/ui/vim/hover.vim | 8 +- autoload/lsp/ui/vim/signature_help.vim | 12 +- autoload/lsp/utils.vim | 74 ++++++++++ ftplugin/lsp-hover.vim | 24 ---- ftplugin/lsp_floatwin.vim | 186 +++++++++++++++++++++++++ syntax/lsp-hover.vim | 36 ----- 10 files changed, 636 insertions(+), 71 deletions(-) create mode 100644 autoload/lsp/ui/vim/floatwin.vim create mode 100644 autoload/lsp/ui/vim/floatwin/nvim.vim create mode 100644 autoload/lsp/ui/vim/floatwin/vim.vim delete mode 100644 ftplugin/lsp-hover.vim create mode 100644 ftplugin/lsp_floatwin.vim delete mode 100644 syntax/lsp-hover.vim diff --git a/autoload/lsp/ui/vim.vim b/autoload/lsp/ui/vim.vim index 1717ecae0..b0bc05670 100644 --- a/autoload/lsp/ui/vim.vim +++ b/autoload/lsp/ui/vim.vim @@ -1,3 +1,7 @@ +let s:Floatwin = lsp#ui#vim#floatwin#import() +let s:floatwin = s:Floatwin.new({ + \ 'close_on': ['InsertEnter', 'CursorMoved'] + \ }) let s:last_req_id = 0 function! s:not_supported(what) abort @@ -506,16 +510,17 @@ function! s:handle_location(ctx, server, type, data) abort "ctx = {counter, list let l:lines = readfile(fnameescape(l:loc['filename'])) if has_key(l:loc,'viewstart') " showing a locationLink let l:view = l:lines[l:loc['viewstart'] : l:loc['viewend']] - call lsp#ui#vim#output#preview(a:server, l:view, { - \ 'statusline': ' LSP Peek ' . a:type, - \ 'filetype': &filetype - \ }) + let l:screenpos = lsp#ui#vim#floatwin#screenpos(line('.'), col('.')) + call s:floatwin.show(l:screenpos, lsp#utils#normalize_markup_content({ + \ 'language': &filetype, + \ 'value': join(l:view, "\n") + \ })) else " showing a location - call lsp#ui#vim#output#preview(a:server, l:lines, { - \ 'statusline': ' LSP Peek ' . a:type, - \ 'cursor': { 'line': l:loc['lnum'], 'col': l:loc['col'], 'align': g:lsp_peek_alignment }, - \ 'filetype': &filetype - \ }) + let l:screenpos = lsp#ui#vim#floatwin#screenpos(line('.'), col('.')) + call s:floatwin.show(l:screenpos, lsp#utils#normalize_markup_content({ + \ 'language': &filetype, + \ 'value': join(l:lines, "\n") + \ })) endif endif endif diff --git a/autoload/lsp/ui/vim/floatwin.vim b/autoload/lsp/ui/vim/floatwin.vim new file mode 100644 index 000000000..b89b0c92d --- /dev/null +++ b/autoload/lsp/ui/vim/floatwin.vim @@ -0,0 +1,183 @@ +let s:floatwin_id = 0 + +let s:namespace = has('nvim') ? 'nvim' : 'vim' + +" +" lsp#ui#vim#floatwin#screenpos +" +function! lsp#ui#vim#floatwin#screenpos(line, col) abort + let l:pos = getpos('.') + let l:scroll_x = (l:pos[2] + l:pos[3]) - wincol() + let l:scroll_y = l:pos[1] - winline() + let l:winpos = win_screenpos(win_getid()) + return [l:winpos[0] + (a:line - l:scroll_y) - 1, l:winpos[1] + (a:col - l:scroll_x) - 1] +endfunction + +" +" lsp#ui#vim#floatwin#import +" +function! lsp#ui#vim#floatwin#import() abort + return s:Floatwin +endfunction + +let s:Floatwin = {} + +" +" new +" +function! s:Floatwin.new(option) abort + let s:floatwin_id += 1 + let l:bufname = printf('lsp_floatwin-%s.lsp_floatwin', s:floatwin_id) + let l:bufnr = bufnr(l:bufname, v:true) + call setbufvar(l:bufnr, '&buflisted', 0) + call setbufvar(l:bufnr, '&buftype', 'nofile') + call setbufvar(l:bufnr, '&filetype', 'lsp_floatwin') + return extend(deepcopy(s:Floatwin), { + \ 'id': s:floatwin_id, + \ 'bufnr': l:bufnr, + \ 'max_width': get(a:option, 'max_width', &columns / 3), + \ 'max_height': get(a:option, 'max_height', &lines / 2), + \ 'close_on': get(a:option, 'close_on', []), + \ 'screenpos': [0, 0], + \ 'contents': [] + \ }) +endfunction + +" +" show_tooltip +" +function! s:Floatwin.show_tooltip(screenpos, contents) abort + let l:width = self.get_width(a:contents) + let l:height = self.get_height(a:contents) + + let l:screenpos = copy(a:screenpos) + let l:screenpos[0] -= 1 + let l:screenpos[1] -= 1 + + " fix height. + if l:screenpos[0] - l:height >= 0 + let l:screenpos[0] -= l:height + else + let l:screenpos[0] += 1 + endif + + " fix width. + if &columns < l:screenpos[1] + l:width + let l:screenpos[1] -= l:screenpos[1] + l:width - &columns + endif + + call self.show(l:screenpos, a:contents) +endfunction + +" +" show +" +function! s:Floatwin.show(screenpos, contents) abort + let self.screenpos = a:screenpos + let self.contents = a:contents + + " create lines. + let l:lines = [] + for l:content in a:contents + let l:lines += l:content + endfor + + " update bufvars. + call setbufvar(self.bufnr, 'lsp_floatwin_lines', l:lines) + + " show or move + call lsp#ui#vim#floatwin#{s:namespace}#show(self) + call setwinvar(self.winid(), '&wrap', 1) + call setwinvar(self.winid(), '&conceallevel', 3) + + " write lines + call lsp#ui#vim#floatwin#{s:namespace}#write(self, l:lines) + + " update syntax highlight. + if has('nvim') && LspFloatwinSyntaxShouldUpdate(self.bufnr) + call lsp#utils#windo(self.winid(), { -> LspFloatwinSyntaxUpdate() }) + endif + + call self.set_close_events() +endfunction + +" +" hide +" +function! s:Floatwin.hide() abort + augroup printf('lsp#ui#vim#floatwin#hide_%s', self.id) + autocmd! + augroup END + call lsp#ui#vim#floatwin#{s:namespace}#hide(self) +endfunction + +" +" enter +" +function! s:Floatwin.enter() abort + call lsp#ui#vim#floatwin#{s:namespace}#enter(self) +endfunction + +" +" is_showing +" +function! s:Floatwin.is_showing() abort + return lsp#ui#vim#floatwin#{s:namespace}#is_showing(self) +endfunction + +" +" winid +" +function! s:Floatwin.winid() abort + return lsp#ui#vim#floatwin#{s:namespace}#winid(self) +endfunction + +" +" set_close_events +" +function! s:Floatwin.set_close_events() abort + let l:close_fn = printf('lsp_floatwin_close_%s', self.id) + let b:[l:close_fn] = { -> self.hide() } + + augroup printf('lsp#ui#vim#floatwin#hide_%s', self.id) + autocmd! + for l:event in self.close_on + execute printf('autocmd %s call b:%s()', l:event, l:close_fn) + endfor + augroup END +endfunction + +" +" get_width +" +function! s:Floatwin.get_width(contents) abort + let l:width = 0 + for l:content in a:contents + let l:width = max([l:width] + map(copy(l:content), { k, v -> strdisplaywidth(v) })) + endfor + + if self.max_width != -1 + return max([min([self.max_width, l:width]), 1]) + endif + return max([l:width, 1]) +endfunction + +" +" get_height +" +function! s:Floatwin.get_height(contents) abort + let l:width = self.get_width(a:contents) + + let l:height = len(a:contents) - 1 + for l:content in a:contents + for l:line in l:content + let l:height += max([1, float2nr(ceil(strdisplaywidth(l:line) / str2float('' . l:width)))]) + endfor + endfor + + if self.max_height != -1 + return max([min([self.max_height, l:height]), 1]) + endif + return max([l:height, 1]) +endfunction + diff --git a/autoload/lsp/ui/vim/floatwin/nvim.vim b/autoload/lsp/ui/vim/floatwin/nvim.vim new file mode 100644 index 000000000..08de798a4 --- /dev/null +++ b/autoload/lsp/ui/vim/floatwin/nvim.vim @@ -0,0 +1,78 @@ +" +" lsp#ui#vim#floatwin#nvim#show +" +function! lsp#ui#vim#floatwin#nvim#show(floatwin) abort + if lsp#ui#vim#floatwin#nvim#is_showing(a:floatwin) + call nvim_win_set_config(a:floatwin.nvim_window, s:get_config(a:floatwin)) + else + let a:floatwin.nvim_window = nvim_open_win(a:floatwin.bufnr, v:false, s:get_config(a:floatwin)) + endif +endfunction + +" +" lsp#ui#vim#floatwin#nvim#hide +" +function! lsp#ui#vim#floatwin#nvim#hide(floatwin) abort + if lsp#ui#vim#floatwin#nvim#is_showing(a:floatwin) + call nvim_win_close(a:floatwin.nvim_window, v:true) + let a:floatwin.nvim_window = v:null + endif +endfunction + +" +" lsp#ui#vim#floatwin#nvim#write +" +function! lsp#ui#vim#floatwin#nvim#write(floatwin, lines) abort + call nvim_buf_set_lines(a:floatwin.bufnr, 0, -1, v:true, a:lines) +endfunction + +" +" lsp#ui#vim#floatwin#nvim#enter +" +function! lsp#ui#vim#floatwin#nvim#enter(floatwin) abort + if lsp#ui#vim#floatwin#nvim#is_showing(a:floatwin) + execute printf('%swincmd w', win_id2win(lsp#ui#vim#floatwin#nvim#winid(a:floatwin))) + endif +endfunction + +" +" lsp#ui#vim#floatwin#nvim#is_showing +" +function! lsp#ui#vim#floatwin#nvim#is_showing(floatwin) abort + if !has_key(a:floatwin,'nvim_window') || a:floatwin.nvim_window is v:null + return v:false + endif + + try + return nvim_win_get_number(a:floatwin.nvim_window) != -1 + catch /.*/ + let a:floatwin.nvim_window = v:null + endtry + return v:false +endfunction + +" +" lsp#ui#vim#floatwin#nvim#winid +" +function! lsp#ui#vim#floatwin#nvim#winid(floatwin) abort + if lsp#ui#vim#floatwin#nvim#is_showing(a:floatwin) + return win_getid(nvim_win_get_number(a:floatwin.nvim_window)) + endif + return -1 +endfunction + +" +" s:get_config +" +function! s:get_config(floatwin) abort + return { + \ 'relative': 'editor', + \ 'width': a:floatwin.get_width(a:floatwin.contents), + \ 'height': a:floatwin.get_height(a:floatwin.contents), + \ 'row': a:floatwin.screenpos[0], + \ 'col': a:floatwin.screenpos[1], + \ 'focusable': v:true, + \ 'style': 'minimal' + \ } +endfunction + diff --git a/autoload/lsp/ui/vim/floatwin/vim.vim b/autoload/lsp/ui/vim/floatwin/vim.vim new file mode 100644 index 000000000..b767e8ee7 --- /dev/null +++ b/autoload/lsp/ui/vim/floatwin/vim.vim @@ -0,0 +1,83 @@ +" +" lsp#ui#vim#floatwin#vim#show +" +function! lsp#ui#vim#floatwin#vim#show(floatwin) abort + if lsp#ui#vim#floatwin#vim#is_showing(a:floatwin) + call popup_move(a:floatwin.vim_winid, s:get_config(a:floatwin)) + else + let a:floatwin.vim_winid = popup_create(a:floatwin.bufnr, s:get_config(a:floatwin)) + endif +endfunction + +" +" lsp#ui#vim#floatwin#vim#hide +" +function! lsp#ui#vim#floatwin#vim#hide(floatwin) abort + try + call popup_hide(a:floatwin.vim_winid) + catch /.*/ + endtry + let a:floatwin.vim_winid = v:null +endfunction + +" +" lsp#ui#vim#floatwin#vim#write +" +function! lsp#ui#vim#floatwin#vim#write(floatwin, lines) abort + call deletebufline(a:floatwin.bufnr, '^', '$') + for l:line in reverse(a:lines) + call appendbufline(a:floatwin.bufnr, 0, l:line) + endfor + call deletebufline(a:floatwin.bufnr, '$') +endfunction + +" +" lsp#ui#vim#floatwin#vim#enter +" +function! lsp#ui#vim#floatwin#vim#enter(floatwin) abort + " noop +endfunction + +" +" lsp#ui#vim#floatwin#vim#is_showing +" +function! lsp#ui#vim#floatwin#vim#is_showing(floatwin) abort + if !has_key(a:floatwin, 'vim_winid') || a:floatwin.vim_winid is v:null + return v:false + endif + + if win_id2win(a:floatwin.vim_winid) == -1 + let a:floatwin.vim_winid = v:null + return v:false + endif + return v:true +endfunction + +" +" lsp#ui#vim#floatwin#vim#winid +" +function! lsp#ui#vim#floatwin#vim#winid(floatwin) abort + if lsp#ui#vim#floatwin#vim#is_showing(a:floatwin) + return a:floatwin.vim_winid + endif + return -1 +endfunction + +" +" s:get_config +" +function! s:get_config(floatwin) abort + return { + \ 'line': a:floatwin.screenpos[0] + 1, + \ 'col': a:floatwin.screenpos[1] + 1, + \ 'pos': 'topleft', + \ 'moved': [0, 100000], + \ 'scrollbar': 0, + \ 'maxwidth': a:floatwin.get_width(a:floatwin.contents), + \ 'maxheight': a:floatwin.get_height(a:floatwin.contents), + \ 'minwidth': a:floatwin.get_width(a:floatwin.contents), + \ 'minheight': a:floatwin.get_height(a:floatwin.contents), + \ 'tabpage': 0 + \ } +endfunction + diff --git a/autoload/lsp/ui/vim/hover.vim b/autoload/lsp/ui/vim/hover.vim index 1633d702d..bbfe72236 100644 --- a/autoload/lsp/ui/vim/hover.vim +++ b/autoload/lsp/ui/vim/hover.vim @@ -1,3 +1,8 @@ +let s:Floatwin = lsp#ui#vim#floatwin#import() +let s:floatwin = s:Floatwin.new({ + \ 'close_on': ['CursorMoved', 'InsertEnter'] + \ }) + function! s:not_supported(what) abort return lsp#utils#error(a:what.' not supported for '.&filetype) endfunction @@ -35,7 +40,8 @@ function! s:handle_hover(server, data) abort endif if !empty(a:data['response']['result']) && !empty(a:data['response']['result']['contents']) - call lsp#ui#vim#output#preview(a:server, a:data['response']['result']['contents'], {'statusline': ' LSP Hover'}) + let l:screenpos = lsp#ui#vim#floatwin#screenpos(line('.'), col('.')) + call s:floatwin.show_tooltip(l:screenpos, lsp#utils#normalize_markup_content(a:data['response']['result']['contents'])) return else call lsp#utils#error('No hover information found') diff --git a/autoload/lsp/ui/vim/signature_help.vim b/autoload/lsp/ui/vim/signature_help.vim index 64e2aff75..d6d3557f8 100644 --- a/autoload/lsp/ui/vim/signature_help.vim +++ b/autoload/lsp/ui/vim/signature_help.vim @@ -1,3 +1,6 @@ +let s:Floatwin = lsp#ui#vim#floatwin#import() +let s:floatwin = s:Floatwin.new({}) + function! s:not_supported(what) abort return lsp#utils#error(a:what.' not supported for '.&filetype) endfunction @@ -75,7 +78,14 @@ function! s:handle_signature_help(server, data) abort call add(l:contents, l:signature['documentation']) endif - call lsp#ui#vim#output#preview(a:server, l:contents, {'statusline': ' LSP SignatureHelp'}) + let l:screenpos = lsp#ui#vim#floatwin#screenpos(line('.'), col('.')) + let l:contents = lsp#utils#normalize_markup_content(l:contents) + if mode()[0] == 'i' + let s:floatwin.close_on = ['InsertLeave', 'CursorMovedI', 'CursorMovedP'] + else + let s:floatwin.close_on = ['InsertEnter', 'CursorMoved'] + endif + call s:floatwin.show(l:screenpos, l:contents) return else " signature help is used while inserting. So this must be graceful. diff --git a/autoload/lsp/utils.vim b/autoload/lsp/utils.vim index 42f1ec178..ef29b8b3a 100644 --- a/autoload/lsp/utils.vim +++ b/autoload/lsp/utils.vim @@ -186,6 +186,80 @@ function! lsp#utils#echo_with_truncation(msg) abort exec 'echo l:msg' endfunction +" +" Safe windo. +" +function! lsp#utils#windo(winid, fn) abort + if !empty(getcmdwintype()) + return + endif + + let l:current_winid = win_getid() + if l:current_winid == a:winid + call a:fn() + return + endif + + let l:mode = mode() + try + execute printf('noautocmd keepalt keepjumps %swindo call a:fn()', win_id2win(a:winid)) + catch /.*/ + call lsp#log('lsp#utils#windo', { 'e': v:exception, 't': v:throwpoint }) + endtry + execute printf('noautocmd keepalt keepjumps %swincmd w', win_id2win(l:current_winid)) + + if index(['v', 'V', "\"], l:mode) >= 0 + normal! gv + endif + if index(['s', 'S', "\"], l:mode) >= 0 + normal! gv + call feedkeys("\", 'n') + endif +endfunction + +" +" Normalize `MarkupContent`, `MarkedString` or `string` to v:t_string. +" +function! lsp#utils#normalize_markup_content(markup_content) abort + let l:normalized = [] + for l:markup_content in type(a:markup_content) == type([]) ? a:markup_content : [a:markup_content] + " string + if type(l:markup_content) == type('') + call add(l:normalized, s:fix_markup_content_string(l:markup_content)) + + " MarkupContent or MarkedString + elseif type(l:markup_content) == type({}) + let l:string = l:markup_content.value + if has_key(l:markup_content, 'language') && l:string !~# '^\(\s\|\n\)*$' + let l:string = printf('``` %s %s ```', l:markup_content.language, l:string) + endif + call add(l:normalized, s:fix_markup_content_string(l:string)) + endif + endfor + return l:normalized +endfunction + +function! s:fix_markup_content_string(string) abort + let l:string = a:string + + " remove \r. + let l:string = substitute(l:string, "\\(\r\n\\|\r\\)", "\n", 'g') + + " compact fenced code block satrting. + let l:string = substitute(l:string, '\%(^\|\n\)\s*\zs\(```\s*\w\+\)\%(\s\|\n\)*', '\1 ', 'g') + + " compact fenced code block ending. + let l:string = substitute(l:string, '\%(\s\|\n\)*```\s*' . '\ze\s*\%($\|\n\)', ' ```', 'g') + + " trim first/last whitespace. + let l:string = substitute(l:string, '^\s*\|\s*$', '', 'g') + + " remove trailing whitspae. + let l:string = substitute(l:string, '\s*\(\s*\%($\|\n\)\)', '\1', 'g') + + return split(l:string, "\n", v:true) +endfunction + " Convert a character-index (0-based) to byte-index (1-based) " This function requires a buffer specifier (expr, see :help bufname()), " a line number (lnum, 1-based), and a character-index (char, 0-based). diff --git a/ftplugin/lsp-hover.vim b/ftplugin/lsp-hover.vim deleted file mode 100644 index 26dbe6ba4..000000000 --- a/ftplugin/lsp-hover.vim +++ /dev/null @@ -1,24 +0,0 @@ -" No usual did_ftplugin header here as we NEED to run this always - -if has('patch-8.1.1517') && g:lsp_preview_float && !has('nvim') - " Can not set buftype or popup_close will fail with 'not a popup window' - setlocal previewwindow bufhidden=wipe noswapfile nobuflisted -else - setlocal previewwindow buftype=nofile bufhidden=wipe noswapfile nobuflisted -endif - -if has('conceal') && b:lsp_do_conceal - setlocal conceallevel=2 -endif - -setlocal nocursorline nofoldenable nonumber norelativenumber - -if has('syntax') - setlocal nospell -endif - -let b:undo_ftplugin = 'setlocal pvw< bt< bh< swf< bl< cul< fen<' . - \ (has('syntax') ? ' spell<' : '') . - \ ' number< relativenumber<' . - \ (has('conceal') && b:lsp_do_conceal ? ' conceallevel<' : '') . - \ ' | unlet! g:markdown_fenced_languages' diff --git a/ftplugin/lsp_floatwin.vim b/ftplugin/lsp_floatwin.vim new file mode 100644 index 000000000..4a09837bc --- /dev/null +++ b/ftplugin/lsp_floatwin.vim @@ -0,0 +1,186 @@ +augroup lsp_floatwin + " This autocmd for support vim's popup-window. + autocmd! + autocmd BufWinEnter * call s:update() +augroup END + +" @see lsp#view#floatwin +function! LspFloatwinSyntaxShouldUpdate(bufnr) abort + if !has_key(b:, 'lsp_floatwin_state') + return v:true + endif + + if !b:lsp_floatwin_state.markdown_syntax + return v:true + endif + + for [l:mark, l:language] in items(s:get_language_map(s:find_marks(a:bufnr))) + if !has_key(b:lsp_floatwin_state.fenced_language_syntaxes, l:language) || + \ !has_key(b:lsp_floatwin_state.fenced_mark_syntaxes, l:mark) + return v:true + endif + endfor + return v:false +endfunction + +" @see lsp#view#floatwin +function! LspFloatwinSyntaxUpdate() + call s:update() +endfunction + +" +" s:update +" +function! s:update() + if &filetype !=# 'lsp_floatwin' + return + endif + + " initialize state. + let b:lsp_floatwin_state = get(b:, 'lsp_floatwin_state', { + \ 'markdown_syntax': v:false, + \ 'fenced_language_syntaxes': {}, + \ 'fenced_mark_syntaxes': {}, + \ }) + + " include markdown syntax. + if !b:lsp_floatwin_state.markdown_syntax + let b:lsp_floatwin_state.markdown_syntax = v:true + + call s:clear() + runtime! syntax/markdown.vim + syntax include @Markdown syntax/markdown.vim + endif + + for [l:mark, l:language] in items(s:get_language_map(s:find_marks(bufnr('%')))) + let l:language_group = printf('@LspMarkdownFenced_%s', s:escape(l:language)) + + " include syntax for language. + if !has_key(b:lsp_floatwin_state.fenced_language_syntaxes, l:language) + let b:lsp_floatwin_state.fenced_language_syntaxes[l:language] = v:true + + try + for l:syntax_path in s:find_syntax_path(l:language) + call s:clear() + execute printf('syntax include %s %s', l:language_group, l:syntax_path) + endfor + catch /.*/ + continue + endtry + endif + + " add highlight and conceal for mark. + if !has_key(b:lsp_floatwin_state.fenced_mark_syntaxes, l:mark) + let b:lsp_floatwin_state.fenced_mark_syntaxes[l:mark] = v:true + + call s:clear() + let l:escaped_mark = s:escape(l:mark) + let l:mark_group = printf('LspMarkdownFencedMark_%s', l:escaped_mark) + let l:mark_start_group = printf('LspMarkdownFencedMarkStart_%s', l:escaped_mark) + let l:mark_end_group = printf('LspMarkdownFencedMarkEnd_%s', l:escaped_mark) + let l:start_mark = printf('^\s*```\s*%s\s*', l:mark) + let l:end_mark = '\s*```\s*$' + execute printf('syntax region %s matchgroup=%s start="%s" matchgroup=%s end="%s" containedin=@Markdown contains=%s keepend concealends', + \ l:mark_group, + \ l:mark_start_group, + \ l:start_mark, + \ l:mark_end_group, + \ l:end_mark, + \ l:language_group + \ ) + endif + endfor +endfunction + +" +" find marks. +" @see autoload/lsp/view/floatwin.vim +" +function! s:find_marks(bufnr) abort + let l:marks = {} + + " find from buffer contents. + let l:text = join(getbufvar(a:bufnr, 'lsp_floatwin_lines', []), "\n") + let l:pos = 0 + while 1 + let l:match = matchlist(l:text, '```\s*\(\w\+\)', l:pos, 1) + if empty(l:match) + break + endif + let l:marks[l:match[1]] = v:true + let l:pos = matchend(l:text, '```\s*\(\w\+\)', l:pos, 1) + endwhile + + return keys(l:marks) +endfunction + +" +" get_syntax_map +" +function! s:get_language_map(marks) abort + let l:language_map = {} + + for l:mark in a:marks + + " resolve from g:markdown_fenced_languages + for l:config in get(g:, 'markdown_fenced_languages', []) + " Supports `let g:markdown_fenced_languages = ['sh']` + if l:config !~# '=' + if l:config ==# l:mark + let l:language_map[l:mark] = l:mark + break + endif + + " Supports `let g:markdown_fenced_languages = ['bash=sh']` + else + let l:config = split(l:config, '=') + if l:config[1] ==# l:mark + let l:language_map[l:config[1]] = l:config[0] + break + endif + endif + endfor + + " add as-is if can't resolved. + if !has_key(l:language_map, l:mark) + let l:language_map[l:mark] = l:mark + endif + endfor + + return l:language_map +endfunction + +" +" find syntax path. +" +function! s:find_syntax_path(name) abort + let l:syntax_paths = [] + for l:rtp in split(&runtimepath, ',') + let l:syntax_path = printf('%s/syntax/%s.vim', l:rtp, a:name) + if filereadable(l:syntax_path) + call add(l:syntax_paths, l:syntax_path) + endif + endfor + return l:syntax_paths +endfunction + +" +" s:escape +" +function! s:escape(group) + let l:group = a:group + let l:group = substitute(l:group, '\.', '_', '') + return l:group +endfunction + +" +" s:clear +" +function! s:clear() + let b:current_syntax = '' + unlet b:current_syntax + + let g:main_syntax = '' + unlet g:main_syntax +endfunction + diff --git a/syntax/lsp-hover.vim b/syntax/lsp-hover.vim deleted file mode 100644 index 099aabd04..000000000 --- a/syntax/lsp-hover.vim +++ /dev/null @@ -1,36 +0,0 @@ -" Converts a markdown language (```foo) to the corresponding Vim filetype -function! s:get_vim_filetype(lang_markdown) abort - " If the user hasn't customised markdown fenced languages, just return the - " markdown language - if !exists('g:markdown_fenced_languages') - return a:lang_markdown - endif - - " Otherwise, check if it has an entry for the given language - for l:type in g:markdown_fenced_languages - let l:vim_type = substitute(matchstr(l:type, '[^=]*$'), '\..*', '', '') - let l:markdown_type = matchstr(l:type, '[^=]*') - - if l:markdown_type ==# a:lang_markdown - " Found entry - return l:vim_type - endif - endfor - - " Not found - return a:lang_markdown -endfunction - -function! s:do_highlight() abort - if exists('b:lsp_syntax_highlights') - for l:entry in b:lsp_syntax_highlights - let l:line = l:entry['line'] - let l:lang = l:entry['language'] - let l:ft = s:get_vim_filetype(l:lang) - - execute printf('syntax match markdownHighlight%s contains=@markdownHighlight%s /^\%%%sl.*$/', l:ft, l:ft, l:line) - endfor - endif -endfunction - -call s:do_highlight()