Skip to content

Commit 943e272

Browse files
committed
small diffs
1 parent c742892 commit 943e272

File tree

2 files changed

+130
-3
lines changed

2 files changed

+130
-3
lines changed

autoload/lsp.vim

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,22 @@ function! s:ensure_conf(buf, server_name, cb) abort
399399
call a:cb(l:msg)
400400
endfunction
401401

402+
let s:file_content = {}
403+
404+
function! s:text_changes(buf) abort
405+
if has_key(s:file_content, a:buf)
406+
let l:old_content = get(s:file_content, a:buf, [])
407+
let l:new_content = getline(1, '$')
408+
let l:changes = lsp#utils#diff#compute(l:old_content, l:new_content)
409+
let s:file_content[a:buf] = l:new_content
410+
else
411+
let l:new_content = getline(1, '$')
412+
let l:changes = {'text': l:new_content}
413+
let s:file_content[a:buf] = l:new_content
414+
endif
415+
return [l:changes]
416+
endfunction
417+
402418
function! s:ensure_changed(buf, server_name, cb) abort
403419
let l:server = s:servers[a:server_name]
404420
let l:path = lsp#utils#get_buffer_uri(a:buf)
@@ -424,9 +440,7 @@ function! s:ensure_changed(buf, server_name, cb) abort
424440
\ 'method': 'textDocument/didChange',
425441
\ 'params': {
426442
\ 'textDocument': s:get_text_document_identifier(a:buf, l:buffer_info),
427-
\ 'contentChanges': [
428-
\ { 'text': s:get_text_document_text(a:buf) },
429-
\ ],
443+
\ 'contentChanges': s:text_changes(a:buf),
430444
\ }
431445
\ })
432446

autoload/lsp/utils/diff.vim

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
" Computes a simplistic diff between [old] and [new].
2+
"
3+
" Returns a dict with keys `range`, `rangeLength`, and `text` matching the LSP
4+
" definition of `TextDocumentContentChangeEvent`.
5+
"
6+
" Finds a single change between the common prefix, and common postfix.
7+
function! lsp#utils#diff#compute(old, new) abort
8+
let [start_line, start_char] = s:FirstDifference(a:old, a:new)
9+
let [end_line, end_char] =
10+
\ s:LastDifference(a:old[start_line:], a:new[start_line:], start_char)
11+
12+
let text = s:ExtractText(a:new, start_line, start_char, end_line, end_char)
13+
let length = s:Length(a:old, start_line, start_char, end_line, end_char)
14+
15+
let adj_end_line = len(a:old) + end_line
16+
let adj_end_char = end_line == 0 ? 0 : strlen(a:old[end_line]) + end_char + 1
17+
18+
let result = { 'range': {'start': {'line': start_line, 'character': start_char},
19+
\ 'end': {'line': adj_end_line, 'character': adj_end_char}},
20+
\ 'text': text,
21+
\ 'rangeLength': length,
22+
\}
23+
24+
return result
25+
endfunction
26+
27+
" Finds the line and character of the first different character between two
28+
" list of Strings.
29+
function! s:FirstDifference(old, new) abort
30+
let line_count = min([len(a:old), len(a:new)])
31+
let i = 0
32+
while i < line_count
33+
if a:old[i] !=# a:new[i] | break | endif
34+
let i += 1
35+
endwhile
36+
if i >= line_count
37+
return [line_count - 1, strlen(a:old[line_count - 1])]
38+
endif
39+
let old_line = a:old[i]
40+
let new_line = a:new[i]
41+
let length = min([strlen(old_line), strlen(new_line)])
42+
let j = 0
43+
while j < length
44+
if old_line[j:j] !=# new_line[j:j] | break | endif
45+
let j += 1
46+
endwhile
47+
return [i, j]
48+
endfunction
49+
50+
function! s:LastDifference(old, new, start_char) abort
51+
let line_count = min([len(a:old), len(a:new)])
52+
if line_count == 0 | return [0, 0] | endif
53+
let i = -1
54+
while i >= -1 * line_count
55+
if a:old[i] !=# a:new[i] | break | endif
56+
let i -= 1
57+
endwhile
58+
if i <= -1 * line_count
59+
let i = -1 * line_count
60+
let old_line = a:old[i][a:start_char:]
61+
let new_line = a:new[i][a:start_char:]
62+
else
63+
let old_line = a:old[i]
64+
let new_line = a:new[i]
65+
endif
66+
let length = min([strlen(old_line), strlen(new_line)])
67+
let j = -1
68+
while j >= -1 * length
69+
if old_line[j:j] !=# new_line[j:j] | break | endif
70+
let j -= 1
71+
endwhile
72+
return [i, j]
73+
endfunction
74+
75+
function! s:ExtractText(lines, start_line, start_char, end_line, end_char) abort
76+
if a:start_line == len(a:lines) + a:end_line
77+
if a:end_line == 0 | return '' | endif
78+
let result = a:lines[a:start_line][a:start_char:a:end_char]
79+
" json_encode treats empty string computed this was as 'null'
80+
if strlen(result) == 0 | let result = '' | endif
81+
return result
82+
endif
83+
let result = a:lines[a:start_line][a:start_char:]."\n"
84+
let adj_end_line = len(a:lines) + a:end_line
85+
for line in a:lines[a:start_line + 1:a:end_line - 1]
86+
let result .= line."\n"
87+
endfor
88+
if a:end_line != 0
89+
let result .= a:lines[a:end_line][:a:end_char]
90+
endif
91+
return result
92+
endfunction
93+
94+
function! s:Length(lines, start_line, start_char, end_line, end_char)
95+
\ abort
96+
let adj_end_line = len(a:lines) + a:end_line
97+
if adj_end_line >= len(a:lines)
98+
let adj_end_char = a:end_char - 1
99+
else
100+
let adj_end_char = strlen(a:lines[adj_end_line]) + a:end_char
101+
endif
102+
if a:start_line == adj_end_line
103+
return adj_end_char - a:start_char + 1
104+
endif
105+
let result = strlen(a:lines[a:start_line]) - a:start_char + 1
106+
let line = a:start_line + 1
107+
while line < adj_end_line
108+
let result += strlen(a:lines[line]) + 1
109+
let line += 1
110+
endwhile
111+
let result += adj_end_char + 1
112+
return result
113+
endfunction

0 commit comments

Comments
 (0)