From 943e2726cbbcfe6aeb3f235113288f9649b126dc Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 13 Jan 2019 00:26:23 +0900 Subject: [PATCH 01/17] small diffs --- autoload/lsp.vim | 20 ++++++- autoload/lsp/utils/diff.vim | 113 ++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 autoload/lsp/utils/diff.vim diff --git a/autoload/lsp.vim b/autoload/lsp.vim index ac3579f55..333ec5f26 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -399,6 +399,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 = getline(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 = getline(1, '$') + let l:changes = {'text': l:new_content} + 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) @@ -424,9 +440,7 @@ function! s:ensure_changed(buf, server_name, cb) abort \ '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), \ } \ }) diff --git a/autoload/lsp/utils/diff.vim b/autoload/lsp/utils/diff.vim new file mode 100644 index 000000000..5fe24abe2 --- /dev/null +++ b/autoload/lsp/utils/diff.vim @@ -0,0 +1,113 @@ +" 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 [start_line, start_char] = s:FirstDifference(a:old, a:new) + let [end_line, end_char] = + \ s:LastDifference(a:old[start_line:], a:new[start_line:], start_char) + + let text = s:ExtractText(a:new, start_line, start_char, end_line, end_char) + let length = s:Length(a:old, start_line, start_char, end_line, end_char) + + let adj_end_line = len(a:old) + end_line + let adj_end_char = end_line == 0 ? 0 : strlen(a:old[end_line]) + end_char + 1 + + let result = { 'range': {'start': {'line': start_line, 'character': start_char}, + \ 'end': {'line': adj_end_line, 'character': adj_end_char}}, + \ 'text': text, + \ 'rangeLength': length, + \} + + return result +endfunction + +" Finds the line and character of the first different character between two +" list of Strings. +function! s:FirstDifference(old, new) abort + let line_count = min([len(a:old), len(a:new)]) + let i = 0 + while i < line_count + if a:old[i] !=# a:new[i] | break | endif + let i += 1 + endwhile + if i >= line_count + return [line_count - 1, strlen(a:old[line_count - 1])] + endif + let old_line = a:old[i] + let new_line = a:new[i] + let length = min([strlen(old_line), strlen(new_line)]) + let j = 0 + while j < length + if old_line[j:j] !=# new_line[j:j] | break | endif + let j += 1 + endwhile + return [i, j] +endfunction + +function! s:LastDifference(old, new, start_char) abort + let line_count = min([len(a:old), len(a:new)]) + if line_count == 0 | return [0, 0] | endif + let i = -1 + while i >= -1 * line_count + if a:old[i] !=# a:new[i] | break | endif + let i -= 1 + endwhile + if i <= -1 * line_count + let i = -1 * line_count + let old_line = a:old[i][a:start_char:] + let new_line = a:new[i][a:start_char:] + else + let old_line = a:old[i] + let new_line = a:new[i] + endif + let length = min([strlen(old_line), strlen(new_line)]) + let j = -1 + while j >= -1 * length + if old_line[j:j] !=# new_line[j:j] | break | endif + let j -= 1 + endwhile + return [i, 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 result = a:lines[a:start_line][a:start_char:a:end_char] + " json_encode treats empty string computed this was as 'null' + if strlen(result) == 0 | let result = '' | endif + return result + endif + let result = a:lines[a:start_line][a:start_char:]."\n" + let adj_end_line = len(a:lines) + a:end_line + for line in a:lines[a:start_line + 1:a:end_line - 1] + let result .= line."\n" + endfor + if a:end_line != 0 + let result .= a:lines[a:end_line][:a:end_char] + endif + return result +endfunction + +function! s:Length(lines, start_line, start_char, end_line, end_char) + \ abort + let adj_end_line = len(a:lines) + a:end_line + if adj_end_line >= len(a:lines) + let adj_end_char = a:end_char - 1 + else + let adj_end_char = strlen(a:lines[adj_end_line]) + a:end_char + endif + if a:start_line == adj_end_line + return adj_end_char - a:start_char + 1 + endif + let result = strlen(a:lines[a:start_line]) - a:start_char + 1 + let line = a:start_line + 1 + while line < adj_end_line + let result += strlen(a:lines[line]) + 1 + let line += 1 + endwhile + let result += adj_end_char + 1 + return result +endfunction From a076247f88d10e0da56e46f4673accc465efe6bd Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 13 Jan 2019 13:11:05 +0900 Subject: [PATCH 02/17] cosmetic changes --- autoload/lsp.vim | 10 +++------- autoload/lsp/utils/diff.vim | 2 ++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 333ec5f26..b5b8c5728 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -404,11 +404,11 @@ 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 = getline(1, '$') + 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 = getline(1, '$') + let l:new_content = getbufline(a:buf, 1, '$') let l:changes = {'text': l:new_content} let s:file_content[a:buf] = l:new_content endif @@ -434,8 +434,6 @@ 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': { @@ -621,9 +619,7 @@ function! s:requires_eol_at_eof(buf) abort 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 diff --git a/autoload/lsp/utils/diff.vim b/autoload/lsp/utils/diff.vim index 5fe24abe2..4961f815b 100644 --- a/autoload/lsp/utils/diff.vim +++ b/autoload/lsp/utils/diff.vim @@ -1,3 +1,5 @@ +" 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 From d37e11e3220688a2f8b6d863fc1e61a96ffe0cdf Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 13 Jan 2019 13:13:31 +0900 Subject: [PATCH 03/17] cosmetic change --- autoload/lsp.vim | 7 ------- 1 file changed, 7 deletions(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index b5b8c5728..bb38a5466 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -611,13 +611,6 @@ 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 return join(getbufline(a:buf, 1, '$'), "\n") endfunction From 34d677a210d93f18a1139501681bfa63214cd330 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 13 Jan 2019 13:24:46 +0900 Subject: [PATCH 04/17] should not be list --- autoload/lsp.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index bb38a5466..07c14f60c 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -409,7 +409,7 @@ function! s:text_changes(buf) abort let s:file_content[a:buf] = l:new_content else let l:new_content = getbufline(a:buf, 1, '$') - let l:changes = {'text': l:new_content} + let l:changes = {'text': join(l:new_content, "\n")} let s:file_content[a:buf] = l:new_content endif return [l:changes] From d400e67219ae7b80593ef2ad4cac4d4cfa0b9990 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 13 Jan 2019 20:42:30 +0900 Subject: [PATCH 05/17] Fixes vint errors --- autoload/lsp/utils/diff.vim | 134 ++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/autoload/lsp/utils/diff.vim b/autoload/lsp/utils/diff.vim index 4961f815b..9e4d50fb7 100644 --- a/autoload/lsp/utils/diff.vim +++ b/autoload/lsp/utils/diff.vim @@ -7,109 +7,109 @@ " " Finds a single change between the common prefix, and common postfix. function! lsp#utils#diff#compute(old, new) abort - let [start_line, start_char] = s:FirstDifference(a:old, a:new) - let [end_line, end_char] = - \ s:LastDifference(a:old[start_line:], a:new[start_line:], start_char) + 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 text = s:ExtractText(a:new, start_line, start_char, end_line, end_char) - let length = s:Length(a:old, start_line, start_char, end_line, end_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 adj_end_line = len(a:old) + end_line - let adj_end_char = end_line == 0 ? 0 : strlen(a:old[end_line]) + end_char + 1 + 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 result = { 'range': {'start': {'line': start_line, 'character': start_char}, - \ 'end': {'line': adj_end_line, 'character': adj_end_char}}, - \ 'text': text, - \ 'rangeLength': length, + 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 result + 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 line_count = min([len(a:old), len(a:new)]) - let i = 0 - while i < line_count - if a:old[i] !=# a:new[i] | break | endif - let i += 1 + 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 >= line_count - return [line_count - 1, strlen(a:old[line_count - 1])] + if i >= l:line_count + return [l:line_count - 1, strlen(a:old[l:line_count - 1])] endif - let old_line = a:old[i] - let new_line = a:new[i] - let length = min([strlen(old_line), strlen(new_line)]) - let j = 0 - while j < length - if old_line[j:j] !=# new_line[j:j] | break | endif - let j += 1 + 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 [i, j] + return [l:i, l:j] endfunction function! s:LastDifference(old, new, start_char) abort - let line_count = min([len(a:old), len(a:new)]) - if line_count == 0 | return [0, 0] | endif - let i = -1 - while i >= -1 * line_count - if a:old[i] !=# a:new[i] | break | endif - let i -= 1 + 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 i <= -1 * line_count - let i = -1 * line_count - let old_line = a:old[i][a:start_char:] - let new_line = a:new[i][a:start_char:] + 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 old_line = a:old[i] - let new_line = a:new[i] + let l:old_line = a:old[l:i] + let l:new_line = a:new[l:i] endif - let length = min([strlen(old_line), strlen(new_line)]) - let j = -1 - while j >= -1 * length - if old_line[j:j] !=# new_line[j:j] | break | endif - let j -= 1 + 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 [i, j] + 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 result = a:lines[a:start_line][a:start_char:a:end_char] + 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(result) == 0 | let result = '' | endif - return result + if strlen(l:result) == 0 | let l:result = '' | endif + return l:result endif - let result = a:lines[a:start_line][a:start_char:]."\n" - let adj_end_line = len(a:lines) + a:end_line - for line in a:lines[a:start_line + 1:a:end_line - 1] - let result .= line."\n" + 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 result .= a:lines[a:end_line][:a:end_char] + let l:result .= a:lines[a:end_line][:a:end_char] endif - return result + return l:result endfunction function! s:Length(lines, start_line, start_char, end_line, end_char) \ abort - let adj_end_line = len(a:lines) + a:end_line - if adj_end_line >= len(a:lines) - let adj_end_char = a:end_char - 1 + 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 adj_end_char = strlen(a:lines[adj_end_line]) + a:end_char + let l:adj_end_char = strlen(a:lines[l:adj_end_line]) + a:end_char endif - if a:start_line == adj_end_line - return adj_end_char - a:start_char + 1 + if a:start_line == l:adj_end_line + return l:adj_end_char - a:start_char + 1 endif - let result = strlen(a:lines[a:start_line]) - a:start_char + 1 - let line = a:start_line + 1 - while line < adj_end_line - let result += strlen(a:lines[line]) + 1 - let line += 1 + 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 result += adj_end_char + 1 - return result + let l:result += l:adj_end_char + 1 + return l:result endfunction From 7fd75b2765bd8dbc87c9dffd3666846c683d141c Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 13 Jan 2019 20:47:36 +0900 Subject: [PATCH 06/17] Fixes vint errors --- autoload/lsp/utils/diff.vim | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/autoload/lsp/utils/diff.vim b/autoload/lsp/utils/diff.vim index 9e4d50fb7..aef00db32 100644 --- a/autoload/lsp/utils/diff.vim +++ b/autoload/lsp/utils/diff.vim @@ -9,7 +9,7 @@ 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) + \ 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) @@ -43,7 +43,7 @@ function! s:FirstDifference(old, new) abort 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 + 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] @@ -59,8 +59,8 @@ function! s:LastDifference(old, new, start_char) abort 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:] + 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] @@ -68,7 +68,7 @@ function! s:LastDifference(old, new, start_char) abort 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 + 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] @@ -77,12 +77,12 @@ 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] + 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: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" From 1acd9063d06535859dc432574eb0d1284971bcac Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 13 Jan 2019 21:51:03 +0900 Subject: [PATCH 07/17] Remove content when buffer is closed --- autoload/lsp.vim | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 07c14f60c..79c4e222e 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -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(: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 @@ -214,7 +214,9 @@ 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) + call remove(s:file_content, l:buf) endfunction function! s:ensure_flush_all(buf, server_names) abort From b68a067ffa0ba391735e83ca3fef2d839295f835 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 13 Jan 2019 21:53:11 +0900 Subject: [PATCH 08/17] Check buffer exists --- autoload/lsp.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 79c4e222e..55fa765d9 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -216,7 +216,9 @@ endfunction function! s:on_text_document_did_close() abort let l:buf = bufnr('%') call lsp#log('s:on_text_document_did_close()', l:buf) - call remove(s:file_content, 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 From b636095965bb6ab0144ae2174d3e4e6b0312c18d Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 13 Jan 2019 22:18:23 +0900 Subject: [PATCH 09/17] Fix typo --- autoload/lsp.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 55fa765d9..4ee3ebc07 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -160,7 +160,7 @@ function! s:on_text_document_did_save() abort 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(:buf, 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 From 72da9c15e28acf05198c62b54df76a3e2fbc0cee Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 14 Jan 2019 22:33:43 +0900 Subject: [PATCH 10/17] Check textDocumentSync --- autoload/lsp.vim | 24 +++++++++++++++++------- autoload/lsp/capabilities.vim | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 4ee3ebc07..cba978ef2 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -405,17 +405,27 @@ endfunction let s:file_content = {} -function! s:text_changes(buf) abort - if has_key(s:file_content, a:buf) +function! s:text_changes(server_name, buf) abort + let l:sync_kind = lsp#capabilities#get_text_document_change_sync_kind(a:server_name) + + " When syncKind is None, return null for contentChanges. + if l:sync_kind == 0 + return v:null + endif + + " When syncKind is Incremental and previous content is saved. + if l:sync_kind == 2 && has_key(s:file_content, a:buf) + " compute diff 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 + return [l:changes] endif + + 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 return [l:changes] endfunction @@ -442,7 +452,7 @@ function! s:ensure_changed(buf, server_name, cb) abort \ 'method': 'textDocument/didChange', \ 'params': { \ 'textDocument': s:get_text_document_identifier(a:buf, l:buffer_info), - \ 'contentChanges': s:text_changes(a:buf), + \ 'contentChanges': s:text_changes(a:server_name, a:buf), \ } \ }) diff --git a/autoload/lsp/capabilities.vim b/autoload/lsp/capabilities.vim index d39715579..6ee0bc899 100644 --- a/autoload/lsp/capabilities.vim +++ b/autoload/lsp/capabilities.vim @@ -65,3 +65,23 @@ function! lsp#capabilities#get_text_document_save_registration_options(server_na endif return [0, { 'includeText': 0 }] endfunction + +" supports_did_change (boolean) +function! lsp#capabilities#get_text_document_change_sync_kind(server_name) abort + let l:capabilities = lsp#get_server_capabilities(a:server_name) + if !empty(l:capabilities) && has_key(l:capabilities, 'textDocumentSync') + if type(l:capabilities['textDocumentSync']) == type({}) + if has_key(l:capabilities['textDocumentSync'], 'change') && type(l:capabilities['textDocumentSync']) == type(1) + let l:val = l:capabilities['textDocumentSync']['change'] + return l:val >= 0 && l:val <= 2 ? l:val : 1 + else + return 1 + endif + elseif type(l:capabilities['textDocumentSync']) == type(1) + return l:capabilities['textDocumentSync'] + else + return 1 + endif + endif + return 1 +endfunction From 5daa6b10045f0974f2b2060484cacfc9b2edea38 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 14 Jan 2019 22:42:11 +0900 Subject: [PATCH 11/17] Use BufWipeout --- autoload/lsp.vim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index cba978ef2..cf366ca1b 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -135,6 +135,7 @@ function! s:register_events() abort autocmd BufReadPost * call s:on_text_document_did_open() autocmd BufWritePost * call s:on_text_document_did_save() autocmd BufWinLeave * call s:on_text_document_did_close() + autocmd BufWipeout * call s:on_buf_wipeout(bufnr('')) autocmd InsertLeave * call s:on_text_document_did_change() autocmd CursorMoved * call s:on_cursor_moved() augroup END @@ -216,8 +217,11 @@ endfunction function! s:on_text_document_did_close() abort 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) +endfunction + +function! s:on_buf_wipeout(buf) abort + if has_key(s:file_content, a:buf) + call remove(s:file_content, a:buf) endif endfunction From a8fb643143bc3866837b99027107bf5cdcd4b227 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 14 Jan 2019 23:11:06 +0900 Subject: [PATCH 12/17] Hold file content per servers --- autoload/lsp.vim | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index cf366ca1b..580bbd606 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -4,6 +4,8 @@ let s:servers = {} " { lsp_id, server_info, init_callbacks, init_result, buffers let s:notification_callbacks = [] " { name, callback } +let s:file_content = {} + " do nothing, place it here only to avoid the message augroup _lsp_silent_ autocmd! @@ -219,9 +221,25 @@ function! s:on_text_document_did_close() abort call lsp#log('s:on_text_document_did_close()', l:buf) endfunction +function! lsp#get_last_file_content(server_name, buf) abort + if has_key(s:file_content, a:buf) && has_key(s:file_content[a:buf], a:server_name) + return s:file_content[a:buf][a:server_name] + endif + return [] +endfunction + +function! lsp#update_file_content(server_name, buf, new) abort + if !has_key(s:file_content, a:buf) + let s:file_content[a:buf] = {} + endif + let s:file_content[a:buf][a:server_name] = a:new +endfunction + function! s:on_buf_wipeout(buf) abort if has_key(s:file_content, a:buf) - call remove(s:file_content, a:buf) + for l:server_name in lsp#get_whitelisted_servers() + call remove(s:file_content[a:buf], l:server_name) + endfor endif endfunction @@ -407,9 +425,8 @@ function! s:ensure_conf(buf, server_name, cb) abort call a:cb(l:msg) endfunction -let s:file_content = {} - function! s:text_changes(server_name, buf) abort + echomsg a:server_name let l:sync_kind = lsp#capabilities#get_text_document_change_sync_kind(a:server_name) " When syncKind is None, return null for contentChanges. @@ -420,16 +437,16 @@ function! s:text_changes(server_name, buf) abort " When syncKind is Incremental and previous content is saved. if l:sync_kind == 2 && has_key(s:file_content, a:buf) " compute diff - let l:old_content = get(s:file_content, a:buf, []) + let l:old_content = lsp#get_last_file_content(a:server_name, 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 + call lsp#update_file_content(a:server_name, a:buf, l:new_content) return [l:changes] endif 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 + call lsp#update_file_content(a:server_name, a:buf, l:new_content) return [l:changes] endfunction From 4c79ed9c52a3ffda2b7207fccad2c2a69eab37ac Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 14 Jan 2019 23:17:10 +0900 Subject: [PATCH 13/17] Add LICENSE-THIRD-PARTY --- LICENSE-THIRD-PARTY | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 LICENSE-THIRD-PARTY diff --git a/LICENSE-THIRD-PARTY b/LICENSE-THIRD-PARTY new file mode 100644 index 000000000..cf15a9c2d --- /dev/null +++ b/LICENSE-THIRD-PARTY @@ -0,0 +1,34 @@ +The vim-lsp source code bundle part of third party's code following which carry +their own copyright notices and license terms: + +* vim-lsc - https://github.com/natebosch/vim-lsc + * autoload/lsc/diff.vim + ==================================================================== + + Copyright 2017 vim-lsc authors + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + From aeb8f08c8666f26854310f098d7431a058385d64 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 14 Jan 2019 23:28:57 +0900 Subject: [PATCH 14/17] Remove debug code --- autoload/lsp.vim | 1 - 1 file changed, 1 deletion(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 580bbd606..2f75aecd2 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -426,7 +426,6 @@ function! s:ensure_conf(buf, server_name, cb) abort endfunction function! s:text_changes(server_name, buf) abort - echomsg a:server_name let l:sync_kind = lsp#capabilities#get_text_document_change_sync_kind(a:server_name) " When syncKind is None, return null for contentChanges. From 6e4104593fcbcf2e464144b5ac9c405a1819323e Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 15 Jan 2019 02:21:20 +0900 Subject: [PATCH 15/17] Comment for file_content --- autoload/lsp.vim | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 2f75aecd2..c788aa722 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -4,6 +4,17 @@ let s:servers = {} " { lsp_id, server_info, init_callbacks, init_result, buffers let s:notification_callbacks = [] " { name, callback } +" This hold previous content for each language servers to make +" DidChangeTextDocumentParams. The key is buffer numbers: +" { +" 1: { +" "golsp": [ "first-line", "next-line", ... ], +" "bingo": [ "first-line", "next-line", ... ] +" }, +" 2: { +" "pyls": [ "first-line", "next-line", ... ] +" } +" } let s:file_content = {} " do nothing, place it here only to avoid the message @@ -221,14 +232,14 @@ function! s:on_text_document_did_close() abort call lsp#log('s:on_text_document_did_close()', l:buf) endfunction -function! lsp#get_last_file_content(server_name, buf) abort +function! s:get_last_file_content(server_name, buf) abort if has_key(s:file_content, a:buf) && has_key(s:file_content[a:buf], a:server_name) return s:file_content[a:buf][a:server_name] endif return [] endfunction -function! lsp#update_file_content(server_name, buf, new) abort +function! s:update_file_content(server_name, buf, new) abort if !has_key(s:file_content, a:buf) let s:file_content[a:buf] = {} endif @@ -237,9 +248,7 @@ endfunction function! s:on_buf_wipeout(buf) abort if has_key(s:file_content, a:buf) - for l:server_name in lsp#get_whitelisted_servers() - call remove(s:file_content[a:buf], l:server_name) - endfor + call remove(s:file_content, a:buf) endif endfunction @@ -436,16 +445,16 @@ function! s:text_changes(server_name, buf) abort " When syncKind is Incremental and previous content is saved. if l:sync_kind == 2 && has_key(s:file_content, a:buf) " compute diff - let l:old_content = lsp#get_last_file_content(a:server_name, a:buf) + let l:old_content = s:get_last_file_content(a:server_name, a:buf) let l:new_content = getbufline(a:buf, 1, '$') let l:changes = lsp#utils#diff#compute(l:old_content, l:new_content) - call lsp#update_file_content(a:server_name, a:buf, l:new_content) + call s:update_file_content(a:server_name, a:buf, l:new_content) return [l:changes] endif let l:new_content = getbufline(a:buf, 1, '$') let l:changes = {'text': join(l:new_content, "\n")} - call lsp#update_file_content(a:server_name, a:buf, l:new_content) + call s:update_file_content(a:server_name, a:buf, l:new_content) return [l:changes] endfunction From 34d20225c5223dc123fb03f92b0ce84fd6dd3a96 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 15 Jan 2019 02:21:37 +0900 Subject: [PATCH 16/17] Note that vim-lsp support incremental changes --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8ef13a7dc..8619dcb4f 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ if executable('typescript-language-server') endif ``` +vim-lsp support incremental changes of Language Server Protocol. + ## auto-complete Refer to docs on configuring omnifunc or [asyncomplete.vim](https://github.com/prabirshrestha/asyncomplete.vim). From 6e1a499960c0c1ce95579b6047228878112d6c23 Mon Sep 17 00:00:00 2001 From: Prabir Shrestha Date: Mon, 14 Jan 2019 11:19:51 -0800 Subject: [PATCH 17/17] use supports --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8619dcb4f..fe238edb3 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ if executable('typescript-language-server') endif ``` -vim-lsp support incremental changes of Language Server Protocol. +vim-lsp supports incremental changes of Language Server Protocol. ## auto-complete