From ef27b622a7a5435006946f66f5d66d671ad1d7de Mon Sep 17 00:00:00 2001 From: hrsh7th Date: Fri, 6 Aug 2021 21:14:58 +0900 Subject: [PATCH] Remove VS.LSP.CompletionItem --- autoload/cmp.vim | 23 --- autoload/vital/_cmp/VS/LSP/CompletionItem.vim | 178 ------------------ autoload/vital/cmp.vital | 5 +- lua/cmp/core.lua | 54 ++++-- lua/cmp/entry.lua | 10 +- lua/cmp/init.lua | 7 - 6 files changed, 46 insertions(+), 231 deletions(-) delete mode 100644 autoload/vital/_cmp/VS/LSP/CompletionItem.vim diff --git a/autoload/cmp.vim b/autoload/cmp.vim index 02b71de..61cad5d 100644 --- a/autoload/cmp.vim +++ b/autoload/cmp.vim @@ -1,6 +1,4 @@ -let s:Position = vital#cmp#import('VS.LSP.Position') let s:TextEdit = vital#cmp#import('VS.LSP.TextEdit') -let s:CompletionItem = vital#cmp#import('VS.LSP.CompletionItem') " " cmp#apply_text_edits @@ -9,24 +7,3 @@ function! cmp#apply_text_edits(bufnr, text_edits) abort call s:TextEdit.apply(a:bufnr, a:text_edits) endfunction -" -" cmp#confirm -" -function! cmp#confirm(args) abort - call s:CompletionItem.confirm({ - \ 'suggest_position': s:Position.vim_to_lsp('%', [line('.'), a:args.suggest_offset]), - \ 'request_position': s:Position.vim_to_lsp('%', [line('.'), a:args.request_offset]), - \ 'current_position': s:Position.vim_to_lsp('%', [line('.'), col('.')]), - \ 'current_line': getline('.'), - \ 'completion_item': a:args.completion_item, - \ 'expand_snippet': s:get_expand_snippet(), - \ }) -endfunction - -" -" get_expand_snippet -" -function! s:get_expand_snippet() abort - return { args -> luaeval('require"cmp"._expand_snippet(_A)', args) } -endfunction - diff --git a/autoload/vital/_cmp/VS/LSP/CompletionItem.vim b/autoload/vital/_cmp/VS/LSP/CompletionItem.vim deleted file mode 100644 index 8ed5834..0000000 --- a/autoload/vital/_cmp/VS/LSP/CompletionItem.vim +++ /dev/null @@ -1,178 +0,0 @@ -" ___vital___ -" NOTE: lines between '" ___vital___' is generated by :Vitalize. -" Do not modify the code nor insert new lines before '" ___vital___' -function! s:_SID() abort - return matchstr(expand(''), '\zs\d\+\ze__SID$') -endfunction -execute join(['function! vital#_cmp#VS#LSP#CompletionItem#import() abort', printf("return map({'_vital_depends': '', 'confirm': '', '_vital_loaded': ''}, \"vital#_cmp#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") -delfunction s:_SID -" ___vital___ -" -" _vital_loaded -" -function! s:_vital_loaded(V) abort - let s:Position = a:V.import('VS.LSP.Position') - let s:TextEdit = a:V.import('VS.LSP.TextEdit') - let s:Text = a:V.import('VS.LSP.Text') -endfunction - -" -" _vital_depends -" -function! s:_vital_depends() abort - return ['VS.LSP.Position', 'VS.LSP.TextEdit', 'VS.LSP.Text'] -endfunction - -" -" confirm -" -" @param {LSP.Position} args.suggest_position -" @param {LSP.Position} args.request_position -" @param {LSP.Position} args.current_position -" @param {string} args.current_line -" @param {LSP.CompletionItem} args.completion_item -" @param {(args: { body: string; insert_text_mode: number; }) => void?} args.expand_snippet -" -" # Pre-condition -" -" - You must pass `current_position` that represents the position when `CompleteDone` was fired. -" - You must pass `current_line` that represents the line when `CompleteDone` was fired. -" - You must call this function after the commit characters has been inserted. -" -" # The positoins -" -" 0. The example case -" -" call getbufl| -> call getbufline| -" -" 1. suggest_position -" -" call |getbufline -" -" 2. request_position -" -" call getbufl|ine -" -" 3. current_position -" -" call getbufline| -" -" -function! s:confirm(args) abort - let l:suggest_position = a:args.suggest_position - let l:request_position = a:args.request_position - let l:current_position = a:args.current_position - let l:current_line = a:args.current_line - let l:completion_item = a:args.completion_item - let l:ExpandSnippet = get(a:args, 'expand_snippet', v:null) - - " 1. Prepare for alignment to VSCode behavior. - let l:expansion = s:_get_expansion({ - \ 'suggest_position': l:suggest_position, - \ 'request_position': l:request_position, - \ 'current_position': l:current_position, - \ 'current_line': l:current_line, - \ 'completion_item': l:completion_item, - \ }) - if !empty(l:expansion) - " Remove commit characters if expansion is needed. - if getline('.') !=# l:current_line - call setline(l:current_position.line + 1, l:current_line) - call cursor(s:Position.lsp_to_vim('%', l:current_position)) - endif - - " Restore state of the timing when `textDocument/completion` was sent. - call s:TextEdit.apply('%', [{ - \ 'range': { 'start': l:request_position, 'end': l:current_position }, - \ 'newText': '' - \ }]) - endif - - " 2. Apply additionalTextEdits - if type(get(l:completion_item, 'additionalTextEdits', v:null)) == type([]) - call s:TextEdit.apply('%', l:completion_item.additionalTextEdits) - endif - - " 3. Apply expansion - if !empty(l:expansion) - let l:current_position = s:Position.cursor() " Update current_position to after additionalTextEdits. - let l:range = { - \ 'start': extend({ - \ 'character': l:current_position.character - l:expansion.overflow_before, - \ }, l:current_position, 'keep'), - \ 'end': extend({ - \ 'character': l:current_position.character + l:expansion.overflow_after, - \ }, l:current_position, 'keep') - \ } - - " Snippet. - if l:expansion.is_snippet && !empty(l:ExpandSnippet) - call s:TextEdit.apply('%', [{ 'range': l:range, 'newText': '' }]) - call cursor(s:Position.lsp_to_vim('%', l:range.start)) - call l:ExpandSnippet({ 'body': l:expansion.new_text, 'insert_text_mode': get(l:completion_item, 'insertTextMode', 2) }) - - " TextEdit. - else - call s:TextEdit.apply('%', [{ 'range': l:range, 'newText': l:expansion.new_text }]) - - " Move cursor position to end of new_text like as snippet. - let l:lines = s:Text.split_by_eol(l:expansion.new_text) - let l:cursor = copy(l:range.start) - let l:cursor.line += len(l:lines) - 1 - let l:cursor.character = strchars(l:lines[-1]) + (len(l:lines) == 1 ? l:cursor.character : 0) - call cursor(s:Position.lsp_to_vim('%', l:cursor)) - endif - endif -endfunction - -" -" _get_expansion -" -function! s:_get_expansion(args) abort - let l:current_line = a:args.current_line - let l:suggest_position = a:args.suggest_position - let l:request_position = a:args.request_position - let l:current_position = a:args.current_position - let l:completion_item = a:args.completion_item - - let l:is_snippet = get(l:completion_item, 'insertTextFormat', 1) == 2 - if type(get(l:completion_item, 'textEdit', v:null)) == type({}) - let l:inserted_text = strcharpart(l:current_line, l:request_position.character, l:current_position.character - l:request_position.character) - let l:overflow_before = l:request_position.character - l:completion_item.textEdit.range.start.character - let l:overflow_after = l:completion_item.textEdit.range.end.character - l:request_position.character - let l:inserted = '' - \ . strcharpart(l:current_line, l:request_position.character - l:overflow_before, l:overflow_before) - \ . strcharpart(l:current_line, l:request_position.character, strchars(l:inserted_text) + l:overflow_after) - let l:new_text = l:completion_item.textEdit.newText - if s:_trim_tabstop(l:new_text) !=# l:inserted - " The LSP spec says `textEdit range must contain the request position.` - return { - \ 'overflow_before': max([0, l:overflow_before]), - \ 'overflow_after': max([0, l:overflow_after]), - \ 'new_text': l:new_text, - \ 'is_snippet': l:is_snippet, - \ } - endif - else - let l:inserted = strcharpart(l:current_line, l:suggest_position.character, l:current_position.character - l:suggest_position.character) - let l:new_text = get(l:completion_item, 'insertText', v:null) - let l:new_text = !empty(l:new_text) ? l:new_text : l:completion_item.label - if s:_trim_tabstop(l:new_text) !=# l:inserted - return { - \ 'overflow_before': l:request_position.character - l:suggest_position.character, - \ 'overflow_after': 0, - \ 'new_text': l:new_text, - \ 'is_snippet': l:is_snippet, - \ } - endif - endif - return {} -endfunction - -" -" _trim_tabstop -" -function! s:_trim_tabstop(text) abort - return substitute(a:text, '\%(\$0\|\${0}\)$', '', 'g') -endfunction - diff --git a/autoload/vital/cmp.vital b/autoload/vital/cmp.vital index 42822c4..1213d2a 100644 --- a/autoload/vital/cmp.vital +++ b/autoload/vital/cmp.vital @@ -1,5 +1,4 @@ cmp -5828301d6bae0858e9ea21012913544f5ef8e375 +2755f0c8fbd3442bcb7f567832e4d1455b57f9a2 -VS.LSP.CompletionItem -VS.LSP.Position +VS.LSP.TextEdit diff --git a/lua/cmp/core.lua b/lua/cmp/core.lua index 9628e8a..2f32ef9 100644 --- a/lua/cmp/core.lua +++ b/lua/cmp/core.lua @@ -205,9 +205,9 @@ core.confirm = vim.schedule_wrap(function(e, option, callback) local has_cursor_line_text_edit = (function() local minrow = math.min(pre.cursor.row, new.cursor.row) local maxrow = math.max(pre.cursor.row, new.cursor.row) - for _, text_edit in ipairs(text_edits) do - local srow = text_edit.range.start.line + 1 - local erow = text_edit.range['end'].line + 1 + for _, te in ipairs(text_edits) do + local srow = te.range.start.line + 1 + local erow = te.range['end'].line + 1 if srow <= minrow and maxrow <= erow then return true end @@ -235,30 +235,48 @@ core.confirm = vim.schedule_wrap(function(e, option, callback) completion_item.textEdit.range = e:get_insert_range() end - -- First, emulates vim's `` behavior and then confirms LSP functionalities. + if #(misc.safe(e:get_completion_item().additionalTextEdits) or {}) > 0 then + vim.fn['cmp#apply_text_edits'](pre.bufnr, e:get_completion_item().additionalTextEdits) + end - local range = types.lsp.Range.to_vim(pre.bufnr, e:get_insert_range()) + -- First, emulates vim's `` behavior and then confirms LSP functionalities. + local range = types.lsp.Range.to_vim(pre.bufnr, completion_item.textEdit.range) local before_text = string.sub(pre.cursor_line, range.start.col, pre.cursor.col - 1) local after_text = string.sub(pre.cursor_line, pre.cursor.col, pre.cursor.col + (range['end'].col - e.context.cursor.col) - 1) local before_len = vim.fn.strchars(before_text) local after_len = vim.fn.strchars(after_text) - local keys = string.rep('U', after_len) .. string.rep('', before_len + after_len) .. e:get_word() + local keys = 'u' .. string.rep('U', after_len) .. string.rep('', before_len + after_len) + if not completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then + keys = keys .. completion_item.textEdit.newText + else + keys = keys .. e:get_word() + end keymap.feedkeys( keys, 'n', vim.schedule_wrap(function() - vim.fn['cmp#confirm']({ - request_offset = e.context.cursor.col, - suggest_offset = e:get_offset(), - completion_item = completion_item, - }) - - -- execute - e:execute(function() - if callback then - callback() - end - end) + local execute = function() + e:execute(function() + if callback then + callback() + end + end) + end + if completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then + keymap.feedkeys( + 'U' .. string.rep('', vim.fn.strchars(e:get_word())), + 'n', + vim.schedule_wrap(function() + config.get().snippet.expand({ + body = completion_item.textEdit.newText, + insert_text_mode = completion_item.insertTextMode, + }) + execute() + end) + ) + else + execute() + end end) ) end) diff --git a/lua/cmp/entry.lua b/lua/cmp/entry.lua index 3f44db5..de5b58d 100644 --- a/lua/cmp/entry.lua +++ b/lua/cmp/entry.lua @@ -190,13 +190,19 @@ end ---@return vim.CompletedItem entry.get_vim_item = function(self, suggeset_offset) return self.cache:ensure({ 'get_vim_item', suggeset_offset }, function() - local diff = vim.str_byteindex(self.context.cursor_line, self:get_replace_range()['end'].character) local item = config.get().formatting.format(self, suggeset_offset) - item.word = str.remove_suffix(item.word, string.sub(self.context.cursor_line, self.context.cursor.col, diff)) item.equal = 1 item.empty = 1 item.dup = self.completion_item.dup or 1 item.user_data = { cmp = self.id } + + for i = 1, #item.word - 1 do + if str.has_prefix(self.context.cursor_after_line, string.sub(item.word, i, #item.word)) then + item.word = string.sub(item.word, 1, i - 1) + break + end + end + return item end) end diff --git a/lua/cmp/init.lua b/lua/cmp/init.lua index b15935c..b6f0352 100644 --- a/lua/cmp/init.lua +++ b/lua/cmp/init.lua @@ -55,13 +55,6 @@ cmp.close = function() core.reset() end ----Internal expand snippet function. ----TODO: It should be removed when we remove `autoload/cmp.vim`. ----@param args cmp.SnippetExpansionParams -cmp._expand_snippet = function(args) - return config.get().snippet.expand(args) -end - ---Handle events autocmd.subscribe('InsertEnter', function() core.prepare()