mirror of
https://github.com/prabirshrestha/vim-lsp.git
synced 2025-12-14 20:35:59 +01:00
Implemented word correction in cases where startcol was corrected by … (#1340)
* Implemented word correction in cases where startcol was corrected by TextEdit.range.start.character * Don't touch abbr * Don't use `..` * relax get_server_info * Improve for deno lsp item * Fix start character * minimal `insertReplaceTextEdit` suppoort. * Change to filter logic similar to asyncomplete-lsp.vim * Improve word creation * relax lsp#utils#text_edit#get_range * Add start_character for test case
This commit is contained in:
@@ -95,7 +95,7 @@ function! lsp#get_server_names() abort
|
||||
endfunction
|
||||
|
||||
function! lsp#get_server_info(server_name) abort
|
||||
return s:servers[a:server_name]['server_info']
|
||||
return get(get(s:servers, a:server_name, {}), 'server_info', {})
|
||||
endfunction
|
||||
|
||||
function! lsp#get_server_root_uri(server_name) abort
|
||||
|
||||
@@ -59,23 +59,10 @@ function! lsp#omni#complete(findstart, base) abort
|
||||
let s:completion['status'] = s:completion_status_pending
|
||||
endif
|
||||
|
||||
" Find first item which has refresh_pattern
|
||||
let l:refresh_pattern = '\(\k\+$\)'
|
||||
for l:server_name in l:info['server_names']
|
||||
let l:server_info = lsp#get_server_info(l:server_name)
|
||||
if has_key(l:server_info, 'config') && has_key(l:server_info['config'], 'refresh_pattern')
|
||||
let l:refresh_pattern = l:server_info['config']['refresh_pattern']
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
let l:curpos = getcurpos()
|
||||
let l:left = strpart(getline(l:curpos[1]), 0, l:curpos[2]-1)
|
||||
let l:left = strpart(getline('.'), 0, col('.')-1)
|
||||
|
||||
" Initialize the default startcol. It will be updated if the completion items has textEdit.
|
||||
let s:completion['startcol'] = 1 + matchstrpos(l:left, l:refresh_pattern)[1]
|
||||
if s:completion['startcol'] == 0
|
||||
let s:completion['startcol'] = strlen(l:left) + 1
|
||||
endif
|
||||
let s:completion['startcol'] = s:get_startcol(l:left, l:info['server_names'])
|
||||
|
||||
" The `l:info` variable will be filled with completion results after request was finished.
|
||||
call s:send_completion_request(l:info)
|
||||
@@ -95,17 +82,6 @@ function! lsp#omni#complete(findstart, base) abort
|
||||
endfunction
|
||||
|
||||
function! s:get_filter_label(item) abort
|
||||
let l:user_data = lsp#omni#get_managed_user_data_from_completed_item(a:item)
|
||||
if has_key(l:user_data, 'completion_item') && has_key(l:user_data['completion_item'], 'filterText')
|
||||
let l:filter_text = l:user_data['completion_item']['filterText']
|
||||
if empty(l:filter_text) && has_key(l:user_data['completion_item'], 'label')
|
||||
" When filterText is `falsy` the label is used as the filter text
|
||||
let l:filter_text = l:user_data['completion_item']['label']
|
||||
endif
|
||||
if !empty(l:filter_text)
|
||||
return lsp#utils#_trim(l:filter_text)
|
||||
endif
|
||||
endif
|
||||
return lsp#utils#_trim(a:item['word'])
|
||||
endfunction
|
||||
|
||||
@@ -298,6 +274,10 @@ function! lsp#omni#get_vim_completion_items(options) abort
|
||||
let l:server_name = l:server['name']
|
||||
let l:kind_text_mappings = s:get_kind_text_mappings(l:server)
|
||||
let l:complete_position = a:options['position']
|
||||
let l:current_line = getline('.')
|
||||
let l:default_startcol = s:get_startcol(strcharpart(l:current_line, 0, l:complete_position['character']), [l:server_name])
|
||||
let l:default_start_character = strchars(strpart(l:current_line, 0, l:default_startcol - 1))
|
||||
let l:refresh_pattern = s:get_refresh_pattern([l:server_name])
|
||||
|
||||
let l:result = a:options['response']['result']
|
||||
if type(l:result) == type([])
|
||||
@@ -319,6 +299,8 @@ function! lsp#omni#get_vim_completion_items(options) abort
|
||||
endif
|
||||
|
||||
let l:start_character = l:complete_position['character']
|
||||
|
||||
let l:start_characters = [] " The mapping of item specific start_character.
|
||||
let l:vim_complete_items = []
|
||||
for l:completion_item in l:items
|
||||
let l:expandable = get(l:completion_item, 'insertTextFormat', 1) == 2
|
||||
@@ -328,13 +310,33 @@ function! lsp#omni#get_vim_completion_items(options) abort
|
||||
\ 'empty': 1,
|
||||
\ 'icase': 1,
|
||||
\ }
|
||||
if has_key(l:completion_item, 'textEdit') && type(l:completion_item['textEdit']) == s:t_dict && has_key(l:completion_item['textEdit'], 'range') && has_key(l:completion_item['textEdit'], 'newText')
|
||||
let l:vim_complete_item['word'] = l:completion_item['textEdit']['newText']
|
||||
let l:start_character = min([l:completion_item['textEdit']['range']['start']['character'], l:start_character])
|
||||
let l:range = lsp#utils#text_edit#get_range(get(l:completion_item, 'textEdit', {}))
|
||||
if has_key(l:completion_item, 'textEdit') && type(l:completion_item['textEdit']) == s:t_dict && !empty(l:range) && has_key(l:completion_item['textEdit'], 'newText')
|
||||
let l:text_edit_new_text = l:completion_item['textEdit']['newText']
|
||||
if has_key(l:completion_item, 'filterText') && !empty(l:completion_item['filterText']) && matchstr(l:text_edit_new_text, '^' . l:refresh_pattern) ==# ''
|
||||
" Use filterText as word.
|
||||
let l:vim_complete_item['word'] = l:completion_item['filterText']
|
||||
let l:start_characters += [l:default_start_character]
|
||||
else
|
||||
" Use textEdit.newText as word.
|
||||
let l:item_start_character = l:range['start']['character']
|
||||
let l:vim_complete_item['word'] = l:text_edit_new_text
|
||||
if l:item_start_character < l:default_start_character
|
||||
" Add already typed word. The typescript-language-server returns `[Symbol]` item for the line of `Hoo.|`. So we should add `.` (`.[Symbol]`) .
|
||||
let l:overlap_text = strcharpart(l:current_line, l:item_start_character, l:default_start_character - l:item_start_character)
|
||||
if stridx(l:vim_complete_item['word'], l:overlap_text) != 0
|
||||
let l:vim_complete_item['word'] = l:overlap_text . l:vim_complete_item['word']
|
||||
endif
|
||||
endif
|
||||
let l:start_character = min([l:item_start_character, l:start_character])
|
||||
let l:start_characters += [l:item_start_character]
|
||||
endif
|
||||
elseif has_key(l:completion_item, 'insertText') && !empty(l:completion_item['insertText'])
|
||||
let l:vim_complete_item['word'] = l:completion_item['insertText']
|
||||
let l:start_characters += [l:default_start_character]
|
||||
else
|
||||
let l:vim_complete_item['word'] = l:completion_item['label']
|
||||
let l:start_characters += [l:default_start_character]
|
||||
endif
|
||||
|
||||
if l:expandable
|
||||
@@ -345,12 +347,22 @@ function! lsp#omni#get_vim_completion_items(options) abort
|
||||
endif
|
||||
|
||||
if s:is_user_data_support
|
||||
let l:vim_complete_item['user_data'] = s:create_user_data(l:completion_item, l:server_name, l:complete_position)
|
||||
let l:vim_complete_item['user_data'] = s:create_user_data(l:completion_item, l:server_name, l:complete_position, l:start_characters[len(l:start_characters) - 1])
|
||||
endif
|
||||
|
||||
let l:vim_complete_items += [l:vim_complete_item]
|
||||
endfor
|
||||
|
||||
" Add the additional text for startcol correction.
|
||||
if l:start_character != l:default_start_character
|
||||
for l:i in range(len(l:start_characters))
|
||||
let l:item_start_character = l:start_characters[l:i]
|
||||
if l:start_character < l:item_start_character
|
||||
let l:item = l:vim_complete_items[l:i]
|
||||
let l:item['word'] = strcharpart(l:current_line, l:start_character, l:item_start_character - l:start_character) . l:item['word']
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
let l:startcol = lsp#utils#position#lsp_character_to_vim('%', { 'line': l:complete_position['line'], 'character': l:start_character })
|
||||
|
||||
return { 'items': l:vim_complete_items, 'incomplete': l:incomplete, 'startcol': l:startcol }
|
||||
@@ -369,12 +381,13 @@ endfunction
|
||||
"
|
||||
" create item's user_data.
|
||||
"
|
||||
function! s:create_user_data(completion_item, server_name, complete_position) abort
|
||||
function! s:create_user_data(completion_item, server_name, complete_position, start_character) abort
|
||||
let l:user_data_key = s:create_user_data_key(s:managed_user_data_key_base)
|
||||
let s:managed_user_data_map[l:user_data_key] = {
|
||||
\ 'complete_position': a:complete_position,
|
||||
\ 'server_name': a:server_name,
|
||||
\ 'completion_item': a:completion_item
|
||||
\ 'completion_item': a:completion_item,
|
||||
\ 'start_character': a:start_character,
|
||||
\ }
|
||||
let s:managed_user_data_key_base += 1
|
||||
return l:user_data_key
|
||||
@@ -419,4 +432,21 @@ endfunction
|
||||
function! s:create_user_data_key(base) abort
|
||||
return '{"vim-lsp/key":"' . a:base . '"}'
|
||||
endfunction
|
||||
|
||||
function! s:get_startcol(left, server_names) abort
|
||||
" Initialize the default startcol. It will be updated if the completion items has textEdit.
|
||||
let l:startcol = 1 + matchstrpos(a:left, s:get_refresh_pattern(a:server_names))[1]
|
||||
return l:startcol == 0 ? strlen(a:left) + 1 : l:startcol
|
||||
endfunction
|
||||
|
||||
function! s:get_refresh_pattern(server_names) abort
|
||||
for l:server_name in a:server_names
|
||||
let l:server_info = lsp#get_server_info(l:server_name)
|
||||
if has_key(l:server_info, 'config') && has_key(l:server_info['config'], 'refresh_pattern')
|
||||
return l:server_info['config']['refresh_pattern']
|
||||
endif
|
||||
endfor
|
||||
return '\(\k\+$\)'
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
@@ -54,6 +54,7 @@ function! s:on_complete_done() abort
|
||||
let s:context['complete_position'] = l:managed_user_data['complete_position']
|
||||
let s:context['server_name'] = l:managed_user_data['server_name']
|
||||
let s:context['completion_item'] = l:managed_user_data['completion_item']
|
||||
let s:context['start_character'] = l:managed_user_data['start_character']
|
||||
call feedkeys(printf("\<C-r>=<SNR>%d_on_complete_done_after()\<CR>", s:SID()), 'n')
|
||||
endfunction
|
||||
|
||||
@@ -75,7 +76,7 @@ function! s:on_complete_done_after() abort
|
||||
let l:complete_position = s:context['complete_position']
|
||||
let l:server_name = s:context['server_name']
|
||||
let l:completion_item = s:context['completion_item']
|
||||
let l:complete_start_character = l:done_position['character'] - strchars(l:completed_item['word'])
|
||||
let l:start_character = s:context['start_character']
|
||||
|
||||
" check the commit characters are <BS> or <C-w>.
|
||||
if strlen(getline('.')) < strlen(l:done_line)
|
||||
@@ -106,8 +107,9 @@ function! s:on_complete_done_after() abort
|
||||
if l:is_expandable
|
||||
" At this timing, the cursor may have been moved by additionalTextEdit, so we use overflow information instead of textEdit itself.
|
||||
if type(get(l:completion_item, 'textEdit', v:null)) == type({})
|
||||
let l:overflow_before = max([0, l:complete_start_character - l:completion_item['textEdit']['range']['start']['character']])
|
||||
let l:overflow_after = max([0, l:completion_item['textEdit']['range']['end']['character'] - l:complete_position['character']])
|
||||
let l:range = lsp#utils#text_edit#get_range(l:completion_item['textEdit'])
|
||||
let l:overflow_before = max([0, l:start_character - l:range['start']['character']])
|
||||
let l:overflow_after = max([0, l:range['end']['character'] - l:complete_position['character']])
|
||||
let l:text = l:completion_item['textEdit']['newText']
|
||||
else
|
||||
let l:overflow_before = 0
|
||||
@@ -120,7 +122,7 @@ function! s:on_complete_done_after() abort
|
||||
let l:range = {
|
||||
\ 'start': {
|
||||
\ 'line': l:position['line'],
|
||||
\ 'character': l:position['character'] - (l:complete_position['character'] - l:complete_start_character) - l:overflow_before,
|
||||
\ 'character': l:position['character'] - (l:complete_position['character'] - l:start_character) - l:overflow_before,
|
||||
\ },
|
||||
\ 'end': {
|
||||
\ 'line': l:position['line'],
|
||||
@@ -160,7 +162,8 @@ endfunction
|
||||
"
|
||||
function! s:is_expandable(done_line, done_position, complete_position, completion_item, completed_item) abort
|
||||
if get(a:completion_item, 'textEdit', v:null) isnot# v:null
|
||||
if a:completion_item['textEdit']['range']['start']['line'] != a:completion_item['textEdit']['range']['end']['line']
|
||||
let l:range = lsp#utils#text_edit#get_range(a:completion_item['textEdit'])
|
||||
if l:range['start']['line'] != l:range['end']['line']
|
||||
return v:true
|
||||
endif
|
||||
|
||||
@@ -168,8 +171,8 @@ function! s:is_expandable(done_line, done_position, complete_position, completio
|
||||
let l:completed_before = strcharpart(a:done_line, 0, a:complete_position['character'])
|
||||
let l:completed_after = strcharpart(a:done_line, a:done_position['character'], strchars(a:done_line) - a:done_position['character'])
|
||||
let l:completed_line = l:completed_before . l:completed_after
|
||||
let l:text_edit_before = strcharpart(l:completed_line, 0, a:completion_item['textEdit']['range']['start']['character'])
|
||||
let l:text_edit_after = strcharpart(l:completed_line, a:completion_item['textEdit']['range']['end']['character'], strchars(l:completed_line) - a:completion_item['textEdit']['range']['end']['character'])
|
||||
let l:text_edit_before = strcharpart(l:completed_line, 0, l:range['start']['character'])
|
||||
let l:text_edit_after = strcharpart(l:completed_line, l:range['end']['character'], strchars(l:completed_line) - l:range['end']['character'])
|
||||
return a:done_line !=# l:text_edit_before . s:trim_unmeaning_tabstop(a:completion_item['textEdit']['newText']) . l:text_edit_after
|
||||
endif
|
||||
return s:get_completion_text(a:completion_item) !=# s:trim_unmeaning_tabstop(a:completed_item['word'])
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
function! lsp#utils#text_edit#get_range(text_edit) abort
|
||||
if type(a:text_edit) != v:t_dict
|
||||
return v:null
|
||||
endif
|
||||
let l:insert = get(a:text_edit, 'insert', v:null)
|
||||
if type(l:insert) == v:t_dict
|
||||
return l:insert
|
||||
endif
|
||||
return get(a:text_edit, 'range', v:null)
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#text_edit#apply_text_edits(uri, text_edits) abort
|
||||
let l:current_bufname = bufname('%')
|
||||
let l:target_bufname = lsp#utils#uri_to_path(a:uri)
|
||||
|
||||
@@ -82,7 +82,8 @@ Describe lsp#omni
|
||||
Assert Equals(lsp#omni#get_managed_user_data_from_completed_item(got['items'][0]), {
|
||||
\ 'server_name': 'dummy-server',
|
||||
\ 'completion_item': item,
|
||||
\ 'complete_position': { 'line': 1, 'character': 1 }
|
||||
\ 'complete_position': { 'line': 1, 'character': 1 },
|
||||
\ 'start_character': 0,
|
||||
\ })
|
||||
End
|
||||
|
||||
@@ -184,7 +185,8 @@ Describe lsp#omni
|
||||
Assert Equals(lsp#omni#get_managed_user_data_from_completed_item(got['items'][0]), {
|
||||
\ 'server_name': 'dummy-server',
|
||||
\ 'completion_item': item,
|
||||
\ 'complete_position': { 'line': 1, 'character': 1 }
|
||||
\ 'complete_position': { 'line': 1, 'character': 1 },
|
||||
\ 'start_character': 0,
|
||||
\ })
|
||||
End
|
||||
|
||||
|
||||
Reference in New Issue
Block a user