Skip to content

Commit

Permalink
Always use method in TextEdit
Browse files Browse the repository at this point in the history
  • Loading branch information
hrsh7th committed Jan 6, 2021
1 parent 761ac6d commit 7db44b6
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 179 deletions.
3 changes: 1 addition & 2 deletions autoload/lsp/internal/linked_editing_range.vim
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ function! lsp#internal#linked_editing_range#_enable() abort
\ lsp#callbag#pipe(
\ lsp#callbag#fromEvent(['TextChanged', 'TextChangedI', 'TextChangedP']),
\ lsp#callbag#filter({ -> g:lsp_linked_editing_range_enabled }),
\ lsp#callbag#delay(0),
\ lsp#callbag#subscribe({ -> s:sync() })
\ ),
\ )
Expand Down Expand Up @@ -60,7 +59,7 @@ function! lsp#internal#linked_editing_range#prepare() abort
endfunction

function! s:enabled(...) abort
return g:lsp_linked_editing_range_enabled && s:TextEdit.is_text_mark_preserved() && s:TextMark.is_available()
return g:lsp_linked_editing_range_enabled && s:TextMark.is_available()
endfunction

function! s:request_sync() abort
Expand Down
217 changes: 42 additions & 175 deletions autoload/vital/_lsp/VS/LSP/TextEdit.vim
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_lsp#VS#LSP#TextEdit#import() abort', printf("return map({'set_method': '', '_vital_depends': '', 'get_method': '', 'is_text_mark_preserved': '', 'apply': '', 'get_methods': '', 'delete': '', '_vital_loaded': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
execute join(['function! vital#_lsp#VS#LSP#TextEdit#import() abort', printf("return map({'_vital_depends': '', 'apply': '', '_vital_loaded': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
"
Expand All @@ -23,144 +23,64 @@ function! s:_vital_depends() abort
return ['VS.LSP.Text', 'VS.LSP.Position', 'VS.Vim.Option']
endfunction

"
" Current selected method.
"
let s:_method = 'auto'

"
" This dict contains some logics for patching text.
"
let s:_methods = {}

"
" set_method
"
function! s:set_method(method) abort
if !has_key(s:_methods, a:method)
let s:_method = 'auto'
elseif a:method ==# 'nvim_buf_set_text' && !exists('*nvim_buf_set_text')
let s:_method = 'auto'
elseif a:method ==# 'normal' && has('nvim')
let s:_method = 'auto'
else
let s:_method = a:method
endif
endfunction

"
" get_method
"
function! s:get_method() abort
if s:_method ==# 'auto'
if exists('*nvim_buf_set_text')
return 'nvim_buf_set_text'
elseif !has('nvim')
return 'normal'
else
return 'function'
endif
endif
return s:_method
endfunction

"
" get_methods
"
function! s:get_methods() abort
return ['nvim_buf_set_text', 'normal', 'function']
endfunction

"
" is_text_mark_preserved
"
function! s:is_text_mark_preserved() abort
return index(['nvim_buf_set_text'], s:get_method()) >= 0
endfunction

"
" apply
"
function! s:apply(path, text_edits) abort
let l:current_bufname = bufname('%')
let l:target_bufname = a:path
let l:cursor_position = s:Position.cursor()
let l:current_position = s:Position.cursor()

let l:target_bufnr = s:_switch(a:path)
try
call s:_switch(a:path)
let [l:has_overflowed, l:text_edits] = s:_normalize(bufnr(l:target_bufname), a:text_edits)
let l:fix_cursor = s:_methods[s:get_method()](bufnr(l:target_bufname), l:text_edits, l:cursor_position)
if l:has_overflowed && getline('$') ==# ''
call s:delete(bufnr(l:target_bufname), '$', '$')
endif
call s:_switch(l:current_bufname)
let l:fix_cursor = s:_substitute(l:target_bufnr, a:text_edits, l:current_position)
catch /.*/
call themis#log(string({ 'exception': v:exception, 'throwpoint': v:throwpoint }))
echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
endtry
let l:current_bufnr = s:_switch(l:current_bufname)

if get(l:, 'fix_cursor', v:false) && bufnr(l:current_bufname) == bufnr(l:target_bufname)
call cursor(s:Position.lsp_to_vim('%', l:cursor_position))
if get(l:, 'fix_cursor', v:false) && l:current_bufnr == l:target_bufnr
call cursor(s:Position.lsp_to_vim('%', l:current_position))
endif
endfunction

let s:_methods = {}

"
" nvim_buf_set_text
"
function! s:_methods.nvim_buf_set_text(bufnr, text_edits, cursor_position) abort
let l:fix_cursor = v:false

for l:text_edit in a:text_edits
let l:start = s:Position.lsp_to_vim(a:bufnr, l:text_edit.range.start)
let l:end = s:Position.lsp_to_vim(a:bufnr, l:text_edit.range.end)
let l:lines = s:Text.split_by_eol(l:text_edit.newText)
call nvim_buf_set_text(
\ a:bufnr,
\ l:start[0] - 1,
\ l:start[1] - 1,
\ l:end[0] - 1,
\ l:end[1] - 1,
\ l:lines
\ )
let l:fix_cursor = s:_fix_cursor(a:cursor_position, l:text_edit, l:lines) || l:fix_cursor
endfor

return l:fix_cursor
endfunction

"
" normal
" _substitute
"
function! s:_methods.normal(bufnr, text_edits, cursor_position) abort
function! s:_substitute(bufnr, text_edits, current_position) abort
let l:fix_cursor = v:false

try
" Save state.
let l:Restore = s:Option.define({
\ 'foldenable': '0',
\ 'virtualedit': 'onemore',
\ 'whichwrap': 'h',
\ 'selection': 'exclusive',
\ })
let l:view = winsaveview()
let l:regx = getreg('x')

for l:text_edit in a:text_edits
" Apply substitute.
let [l:fixeol, l:text_edits] = s:_normalize(a:bufnr, a:text_edits)
for l:text_edit in l:text_edits
let l:start = s:Position.lsp_to_vim(a:bufnr, l:text_edit.range.start)
let l:end = s:Position.lsp_to_vim(a:bufnr, l:text_edit.range.end)
if l:start[0] != l:end[0] || l:start[1] != l:end[1]
let l:command = printf('%sG%s|v%sG%s|"_d', l:start[0], l:start[1], l:end[0], l:end[1])
else
let l:command = printf('%sG%s|', l:start[0], l:start[1])
endif
call setreg('x', s:Text.normalize_eol(l:text_edit.newText), 'c')
execute printf('noautocmd keepjumps normal! %s"xP', l:command)

let l:fix_cursor = s:_fix_cursor(a:cursor_position, l:text_edit, s:Text.split_by_eol(l:text_edit.newText)) || l:fix_cursor
execute printf('noautocmd keepjumps %ssubstitute/\%%%sl\%%%sc\zs\_.\{-}\ze\%%%sl\%%%sc/\=getreg("x")/',
\ l:start[0],
\ l:start[0],
\ l:start[1],
\ l:end[0],
\ l:end[1]
\ )
let l:fix_cursor = s:_fix_cursor(a:current_position, l:text_edit, getreg('x', 1, v:true)) || l:fix_cursor
endfor

" Remove last empty line if fixeol enabled.
if l:fixeol && getline('$') == ''
$delete _
endif
catch /.*/
echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
finally
" Restore state.
call l:Restore()
call winrestview(l:view)
call setreg('x', l:regx)
Expand All @@ -169,50 +89,6 @@ function! s:_methods.normal(bufnr, text_edits, cursor_position) abort
return l:fix_cursor
endfunction

"
" function
"
function! s:_methods.function(bufnr, text_edits, cursor_position) abort
let l:fix_cursor = v:false

for l:text_edit in a:text_edits
let l:start_line = getline(l:text_edit.range.start.line + 1)
let l:end_line = getline(l:text_edit.range.end.line + 1)
let l:before_line = strcharpart(l:start_line, 0, l:text_edit.range.start.character)
let l:after_line = strcharpart(l:end_line, l:text_edit.range.end.character, strchars(l:end_line) - l:text_edit.range.end.character)

" create lines.
let l:lines = s:Text.split_by_eol(l:text_edit.newText)
let l:lines[0] = l:before_line . l:lines[0]
let l:lines[-1] = l:lines[-1] . l:after_line

" save length.
let l:lines_len = len(l:lines)
let l:range_len = (l:text_edit.range.end.line - l:text_edit.range.start.line) + 1

" append or delete lines.
if l:lines_len > l:range_len
call append(l:text_edit.range.end.line, repeat([''], l:lines_len - l:range_len))
elseif l:lines_len < l:range_len
call s:delete(a:bufnr, l:text_edit.range.start.line + l:lines_len, l:text_edit.range.end.line)
endif

" set lines.
let l:i = 0
while l:i < len(l:lines)
let l:lnum = l:text_edit.range.start.line + l:i + 1
if get(getbufline(a:bufnr, l:lnum), 0, v:null) !=# l:lines[l:i]
call setline(l:lnum, l:lines[l:i])
endif
let l:i += 1
endwhile

let l:fix_cursor = s:_fix_cursor(a:cursor_position, l:text_edit, s:Text.split_by_eol(l:text_edit.newText))
endfor

return l:fix_cursor
endfunction

"
" _fix_cursor
"
Expand Down Expand Up @@ -240,7 +116,7 @@ endfunction
function! s:_normalize(bufnr, text_edits) abort
let l:text_edits = type(a:text_edits) == type([]) ? a:text_edits : [a:text_edits]
let l:text_edits = s:_range(l:text_edits)
let l:text_edits = sort(copy(l:text_edits), function('s:_compare', [], {}))
let l:text_edits = sort(l:text_edits, function('s:_compare'))
let l:text_edits = s:_check(l:text_edits)
let l:text_edits = reverse(l:text_edits)
return s:_fix_text_edits(a:bufnr, l:text_edits)
Expand All @@ -250,15 +126,20 @@ endfunction
" _range
"
function! s:_range(text_edits) abort
let l:text_edits = []
for l:text_edit in a:text_edits
if type(l:text_edit) != type({})
continue
endif
if l:text_edit.range.start.line > l:text_edit.range.end.line || (
\ l:text_edit.range.start.line == l:text_edit.range.end.line &&
\ l:text_edit.range.start.character > l:text_edit.range.end.character
\ )
let l:text_edit.range = { 'start': l:text_edit.range.end, 'end': l:text_edit.range.start }
endif
let l:text_edits += [l:text_edit]
endfor
return a:text_edits
return l:text_edits
endfunction

"
Expand Down Expand Up @@ -298,49 +179,35 @@ function! s:_fix_text_edits(bufnr, text_edits) abort
let l:buf = getbufline(a:bufnr, '^', '$')
let l:max = len(l:buf)

let l:has_overflowed = v:false
let l:fixeol = v:false
let l:text_edits = []
for l:text_edit in a:text_edits
if l:max <= l:text_edit.range.start.line
let l:text_edit.range.start.line = l:max - 1
let l:text_edit.range.start.character = strchars(get(l:buf, -1, 0))
let l:text_edit.newText = "\n" . l:text_edit.newText
let l:has_overflowed = v:true
let l:fixeol = &fixendofline && !&binary
endif
if l:max <= l:text_edit.range.end.line
let l:text_edit.range.end.line = l:max - 1
let l:text_edit.range.end.character = strchars(get(l:buf, -1, 0))
let l:has_overflowed = v:true
let l:fixeol = &fixendofline && !&binary
endif
call add(l:text_edits, l:text_edit)
endfor

return [l:has_overflowed, l:text_edits]
return [l:fixeol, l:text_edits]
endfunction

"
" _switch
"
function! s:_switch(path) abort
if bufnr(a:path) >= 0
execute printf('keepalt keepjumps %sbuffer!', bufnr(a:path))
execute printf('noautocmd keepalt keepjumps %sbuffer!', bufnr(a:path))
else
execute printf('keepalt keepjumps edit! %s', fnameescape(a:path))
execute printf('noautocmd keepalt keepjumps edit! %s', fnameescape(a:path))
endif
return bufnr('%')
endfunction

"
" delete
"
function! s:delete(bufnr, start, end) abort
if exists('*deletebufline')
call deletebufline(a:bufnr, a:start, a:end)
else
try
let l:Restore = s:Option.define({ 'foldenable': '0' })
execute printf('%s,%sdelete _', a:start, a:end)
finally
call l:Restore()
endtry
endif
endfunction
2 changes: 0 additions & 2 deletions doc/vim-lsp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -687,8 +687,6 @@ g:lsp_linked_editing_enabled *g:lsp_linked_editing_enabled*

Enable textDocument/linkedEditingRange feature.

NOTE: This only works on the environment has `nvim_buf_set_text` for now.

Example: >
let g:lsp_linked_editing_enabled = 1
let g:lsp_linked_editing_enabled = 0
Expand Down

0 comments on commit 7db44b6

Please sign in to comment.