Skip to content
Merged
47 changes: 27 additions & 20 deletions autoload/lsp.vim
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,18 @@ function! s:on_text_document_did_open() abort
endfunction

function! s:on_text_document_did_save() abort
call lsp#log('s:on_text_document_did_save()', bufnr('%'))
let l:buf = bufnr('%')
call lsp#log('s:on_text_document_did_save()', l:buf)
for l:server_name in lsp#get_whitelisted_servers()
call s:ensure_flush(bufnr('%'), l:server_name, {result->s:call_did_save(l:buf, l:server_name, result, function('s:Noop'))})
call s:ensure_flush(l:buf, l:server_name, {result->s:call_did_save(l:buf, l:server_name, result, function('s:Noop'))})
endfor
endfunction

function! s:on_text_document_did_change() abort
call lsp#log('s:on_text_document_did_change()', bufnr('%'))
let l:buf = bufnr('%')
call lsp#log('s:on_text_document_did_change()', l:buf)
for l:server_name in lsp#get_whitelisted_servers()
call s:ensure_flush(bufnr('%'), l:server_name, function('s:Noop'))
call s:ensure_flush(l:buf, l:server_name, function('s:Noop'))
endfor
endfunction

Expand Down Expand Up @@ -214,7 +214,11 @@ function! s:call_did_save(buf, server_name, result, cb) abort
endfunction

function! s:on_text_document_did_close() abort
call lsp#log('s:on_text_document_did_close()', bufnr('%'))
let l:buf = bufnr('%')
call lsp#log('s:on_text_document_did_close()', l:buf)
if has_key(s:file_content, l:buf)
call remove(s:file_content, l:buf)
endif
endfunction

function! s:ensure_flush_all(buf, server_names) abort
Expand Down Expand Up @@ -399,6 +403,22 @@ function! s:ensure_conf(buf, server_name, cb) abort
call a:cb(l:msg)
endfunction

let s:file_content = {}

function! s:text_changes(buf) abort
if has_key(s:file_content, a:buf)
let l:old_content = get(s:file_content, a:buf, [])
let l:new_content = getbufline(a:buf, 1, '$')
let l:changes = lsp#utils#diff#compute(l:old_content, l:new_content)
let s:file_content[a:buf] = l:new_content
else
let l:new_content = getbufline(a:buf, 1, '$')
let l:changes = {'text': join(l:new_content, "\n")}
let s:file_content[a:buf] = l:new_content
endif
return [l:changes]
endfunction

function! s:ensure_changed(buf, server_name, cb) abort
let l:server = s:servers[a:server_name]
let l:path = lsp#utils#get_buffer_uri(a:buf)
Expand All @@ -418,15 +438,11 @@ function! s:ensure_changed(buf, server_name, cb) abort
let l:buffer_info['changed_tick'] = l:changed_tick
let l:buffer_info['version'] = l:buffer_info['version'] + 1

" todo: support range in contentChanges

call s:send_notification(a:server_name, {
\ 'method': 'textDocument/didChange',
\ 'params': {
\ 'textDocument': s:get_text_document_identifier(a:buf, l:buffer_info),
\ 'contentChanges': [
\ { 'text': s:get_text_document_text(a:buf) },
\ ],
\ 'contentChanges': s:text_changes(a:buf),
\ }
\ })

Expand Down Expand Up @@ -599,17 +615,8 @@ function! lsp#get_whitelisted_servers(...) abort
return l:active_servers
endfunction

function! s:requires_eol_at_eof(buf) abort
let l:file_ends_with_eol = getbufvar(a:buf, '&eol')
let l:vim_will_save_with_eol = !getbufvar(a:buf, '&binary') &&
\ getbufvar(a:buf, '&fixeol')
return l:file_ends_with_eol || l:vim_will_save_with_eol
endfunction

function! s:get_text_document_text(buf) abort
let l:buf_fileformat = getbufvar(a:buf, '&fileformat')
let l:eol = {'unix': "\n", 'dos': "\r\n", 'mac': "\r"}[l:buf_fileformat]
return join(getbufline(a:buf, 1, '$'), l:eol).(s:requires_eol_at_eof(a:buf) ? l:eol : '')
return join(getbufline(a:buf, 1, '$'), "\n")
endfunction

function! s:get_text_document(buf, buffer_info) abort
Expand Down
115 changes: 115 additions & 0 deletions autoload/lsp/utils/diff.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
" This is copied from https://github.com/natebosch/vim-lsc/blob/master/autoload/lsc/diff.vim
"
" Computes a simplistic diff between [old] and [new].
"
" Returns a dict with keys `range`, `rangeLength`, and `text` matching the LSP
" definition of `TextDocumentContentChangeEvent`.
"
" Finds a single change between the common prefix, and common postfix.
function! lsp#utils#diff#compute(old, new) abort
let [l:start_line, l:start_char] = s:FirstDifference(a:old, a:new)
let [l:end_line, l:end_char] =
\ s:LastDifference(a:old[l:start_line :], a:new[l:start_line :], l:start_char)

let l:text = s:ExtractText(a:new, l:start_line, l:start_char, l:end_line, l:end_char)
let l:length = s:Length(a:old, l:start_line, l:start_char, l:end_line, l:end_char)

let l:adj_end_line = len(a:old) + l:end_line
let l:adj_end_char = l:end_line == 0 ? 0 : strlen(a:old[l:end_line]) + l:end_char + 1

let l:result = { 'range': {'start': {'line': l:start_line, 'character': l:start_char},
\ 'end': {'line': l:adj_end_line, 'character': l:adj_end_char}},
\ 'text': l:text,
\ 'rangeLength': l:length,
\}

return l:result
endfunction

" Finds the line and character of the first different character between two
" list of Strings.
function! s:FirstDifference(old, new) abort
let l:line_count = min([len(a:old), len(a:new)])
let l:i = 0
while l:i < l:line_count
if a:old[l:i] !=# a:new[l:i] | break | endif
let l:i += 1
endwhile
if i >= l:line_count
return [l:line_count - 1, strlen(a:old[l:line_count - 1])]
endif
let l:old_line = a:old[l:i]
let l:new_line = a:new[l:i]
let l:length = min([strlen(l:old_line), strlen(l:new_line)])
let l:j = 0
while l:j < l:length
if l:old_line[l:j : l:j] !=# l:new_line[l:j : l:j] | break | endif
let l:j += 1
endwhile
return [l:i, l:j]
endfunction

function! s:LastDifference(old, new, start_char) abort
let l:line_count = min([len(a:old), len(a:new)])
if l:line_count == 0 | return [0, 0] | endif
let l:i = -1
while l:i >= -1 * l:line_count
if a:old[l:i] !=# a:new[l:i] | break | endif
let l:i -= 1
endwhile
if l:i <= -1 * l:line_count
let l:i = -1 * l:line_count
let l:old_line = a:old[l:i][a:start_char :]
let l:new_line = a:new[l:i][a:start_char :]
else
let l:old_line = a:old[l:i]
let l:new_line = a:new[l:i]
endif
let l:length = min([strlen(l:old_line), strlen(l:new_line)])
let l:j = -1
while l:j >= -1 * l:length
if l:old_line[l:j : l:j] !=# l:new_line[l:j : l:j] | break | endif
let l:j -= 1
endwhile
return [l:i, l:j]
endfunction

function! s:ExtractText(lines, start_line, start_char, end_line, end_char) abort
if a:start_line == len(a:lines) + a:end_line
if a:end_line == 0 | return '' | endif
let l:result = a:lines[a:start_line][a:start_char : a:end_char]
" json_encode treats empty string computed this was as 'null'
if strlen(l:result) == 0 | let l:result = '' | endif
return l:result
endif
let l:result = a:lines[a:start_line][a:start_char :]."\n"
let l:adj_end_line = len(a:lines) + a:end_line
for l:line in a:lines[a:start_line + 1:a:end_line - 1]
let l:result .= l:line."\n"
endfor
if a:end_line != 0
let l:result .= a:lines[a:end_line][:a:end_char]
endif
return l:result
endfunction

function! s:Length(lines, start_line, start_char, end_line, end_char)
\ abort
let l:adj_end_line = len(a:lines) + a:end_line
if l:adj_end_line >= len(a:lines)
let l:adj_end_char = a:end_char - 1
else
let l:adj_end_char = strlen(a:lines[l:adj_end_line]) + a:end_char
endif
if a:start_line == l:adj_end_line
return l:adj_end_char - a:start_char + 1
endif
let l:result = strlen(a:lines[a:start_line]) - a:start_char + 1
let l:line = a:start_line + 1
while l:line < l:adj_end_line
let l:result += strlen(a:lines[l:line]) + 1
let l:line += 1
endwhile
let l:result += l:adj_end_char + 1
return l:result
endfunction