@@ -14,108 +14,159 @@ function M.clear_suggestion(bufnr, ns_id)
14
14
vim .b [bufnr ].nes_jump = false
15
15
return
16
16
end
17
- vim . api . nvim_buf_clear_namespace (bufnr , ns_id , 0 , - 1 )
17
+ _dismiss_suggestion (bufnr , ns_id )
18
18
--- @type copilotlsp.InlineEdit
19
19
local state = vim .b [bufnr ].nes_state
20
20
if not state then
21
21
return
22
22
end
23
23
24
- _dismiss_suggestion (bufnr , ns_id )
25
24
vim .b [bufnr ].nes_state = nil
26
25
end
27
26
28
- local function trim_end (s )
29
- return s :gsub (" %s+$" , " " )
30
- end
31
-
32
27
--- @private
33
- --- @param suggestion copilotlsp.InlineEdit
34
- --- @return copilotlsp.nes.LineCalculationResult
35
- function M ._calculate_lines (suggestion )
36
- local deleted_lines_count = suggestion .range [" end" ].line - suggestion .range .start .line
37
- local added_lines = vim .split (trim_end (suggestion .newText ), " \n " )
38
- local added_lines_count = suggestion .newText == " " and 0 or # added_lines
39
- local same_line = 0
40
-
41
- if deleted_lines_count == 0 and added_lines_count == 1 then
42
- --- changing within line
43
- deleted_lines_count = 1
44
- same_line = 1
28
+ --- @param bufnr integer
29
+ --- @param edit lsp.TextEdit
30
+ --- @return copilotlsp.nes.InlineEditPreview
31
+ function M ._calculate_preview (bufnr , edit )
32
+ local text = edit .newText
33
+ local range = edit .range
34
+ local start_line = range .start .line
35
+ local start_char = range .start .character
36
+ local end_line = range [" end" ].line
37
+ local end_char = range [" end" ].character
38
+
39
+ -- Split text by newline. Use plain=true to handle trailing newline correctly.
40
+ local new_lines = vim .split (text , " \n " , { plain = true })
41
+ local num_new_lines = # new_lines
42
+
43
+ local old_lines = vim .api .nvim_buf_get_lines (bufnr , start_line , end_line + 1 , false )
44
+ local num_old_lines = # old_lines
45
+
46
+ local is_same_line = start_line == end_line
47
+ local is_deletion = text == " "
48
+ local lines_edit = is_same_line or (start_char == 0 and end_char == 0 )
49
+ local is_insertion = is_same_line and start_char == end_char
50
+
51
+ if is_deletion and is_insertion then
52
+ -- no-op
53
+ return {}
45
54
end
46
55
47
- -- if
48
- -- suggestion.range.start.line == suggestion.range["end"].line
49
- -- and suggestion.range.start.character == suggestion.range["end"].character
50
- -- then
51
- -- --add only
52
- -- TODO: Do we need to position specifically for add only?
53
- -- UI tests seem to say no
54
- -- end
55
-
56
- -- Calculate positions for delete highlight extmark
57
- --- @type copilotlsp.nes.DeleteExtmark
58
- local delete_extmark = {
59
- row = suggestion .range .start .line ,
60
- end_row = (
61
- suggestion .range [" end" ].character ~= 0 and suggestion .range [" end" ].line + 1
62
- or suggestion .range [" end" ].line
63
- ),
64
- }
56
+ if is_deletion and lines_edit then
57
+ return {
58
+ deletion = {
59
+ range = edit .range ,
60
+ },
61
+ }
62
+ end
65
63
66
- -- Calculate positions for virtual lines extmark
67
- --- @type copilotlsp.nes.AddExtmark
68
- local virt_lines_extmark = {
69
- row = (
70
- suggestion .range [" end" ].character ~= 0 and suggestion .range [" end" ].line
71
- or suggestion .range [" end" ].line - 1
72
- ),
73
- virt_lines_count = added_lines_count ,
74
- }
64
+ if is_insertion and num_new_lines == 1 and text ~= " " then
65
+ -- inline insertion
66
+ return {
67
+ inline_insertion = {
68
+ text = text ,
69
+ line = start_line ,
70
+ character = start_char ,
71
+ },
72
+ }
73
+ end
74
+
75
+ if is_insertion and num_new_lines > 1 then
76
+ if start_char == # old_lines [1 ] and new_lines [1 ] == " " then
77
+ -- insert lines after the start line
78
+ return {
79
+ lines_insertion = {
80
+ text = table.concat (vim .list_slice (new_lines , 2 ), " \n " ),
81
+ line = start_line ,
82
+ },
83
+ }
84
+ end
85
+
86
+ if end_char == 0 and new_lines [num_new_lines ] == " " then
87
+ -- insert lines before the end line
88
+ return {
89
+ lines_insertion = {
90
+ text = table.concat (vim .list_slice (new_lines , 1 , num_new_lines - 1 ), " \n " ),
91
+ line = start_line ,
92
+ above = true ,
93
+ },
94
+ }
95
+ end
96
+ end
97
+
98
+ -- insert lines in the middle
99
+ local prefix = old_lines [1 ]:sub (1 , start_char )
100
+ local suffix = old_lines [num_old_lines ]:sub (end_char + 1 )
101
+ local new_lines_extend = vim .deepcopy (new_lines )
102
+ new_lines_extend [1 ] = prefix .. new_lines_extend [1 ]
103
+ new_lines_extend [num_new_lines ] = new_lines_extend [num_new_lines ] .. suffix
104
+ local insertion = table.concat (new_lines_extend , " \n " )
75
105
76
106
return {
77
- deleted_lines_count = deleted_lines_count ,
78
- added_lines = added_lines ,
79
- added_lines_count = added_lines_count ,
80
- same_line = same_line ,
81
- delete_extmark = delete_extmark ,
82
- virt_lines_extmark = virt_lines_extmark ,
107
+ deletion = {
108
+ range = {
109
+ start = { line = start_line , character = 0 },
110
+ [" end" ] = { line = end_line , character = # old_lines [num_old_lines ] },
111
+ },
112
+ },
113
+ lines_insertion = {
114
+ text = insertion ,
115
+ line = end_line ,
116
+ },
83
117
}
84
118
end
85
119
86
120
--- @private
87
- --- @param edits copilotlsp.InlineEdit[]
121
+ --- @param bufnr integer
88
122
--- @param ns_id integer
89
- function M . _display_next_suggestion ( edits , ns_id )
90
- local state = vim . b [ vim . api . nvim_get_current_buf ()]. nes_state
91
- if state then
92
- M . clear_suggestion ( vim . api . nvim_get_current_buf (), ns_id )
93
- end
94
-
95
- if not edits or # edits == 0 then
96
- -- vim.notify("No suggestion available", vim.log.levels.INFO)
97
- return
123
+ --- @param preview copilotlsp.nes.InlineEditPreview
124
+ function M . _display_preview ( bufnr , ns_id , preview )
125
+ if preview . deletion then
126
+ local range = preview . deletion . range
127
+ vim . api . nvim_buf_set_extmark ( bufnr , ns_id , range . start . line , range . start . character , {
128
+ hl_group = " CopilotLspNesDelete " ,
129
+ end_row = range [ " end " ]. line ,
130
+ end_col = range [ " end " ]. character ,
131
+ })
98
132
end
99
- local bufnr = vim .uri_to_bufnr (edits [1 ].textDocument .uri )
100
- local suggestion = edits [1 ]
101
133
102
- local lines = M . _calculate_lines ( suggestion )
103
-
104
- if lines . deleted_lines_count > 0 then
105
- -- Deleted range red highlight
106
- vim .api .nvim_buf_set_extmark (bufnr , ns_id , lines . delete_extmark . row , 0 , {
107
- hl_group = " CopilotLspNesDelete " ,
108
- end_row = lines . delete_extmark . end_row ,
134
+ local inline_insertion = preview . inline_insertion
135
+ if inline_insertion then
136
+ local virt_lines =
137
+ require ( " copilot-lsp.util " ). hl_text_to_virt_lines ( inline_insertion . text , vim . bo [ bufnr ]. filetype )
138
+ vim .api .nvim_buf_set_extmark (bufnr , ns_id , inline_insertion . line , inline_insertion . character , {
139
+ virt_text = virt_lines [ 1 ] ,
140
+ virt_text_pos = " inline " ,
109
141
})
110
142
end
111
- if lines .added_lines_count > 0 then
112
- local text = trim_end (edits [1 ].text )
113
- local virt_lines = require (" copilot-lsp.util" ).hl_text_to_virt_lines (text , vim .bo [bufnr ].filetype )
114
143
115
- vim .api .nvim_buf_set_extmark (bufnr , ns_id , lines .virt_lines_extmark .row , 0 , {
144
+ local lines_insertion = preview .lines_insertion
145
+ if lines_insertion then
146
+ local virt_lines =
147
+ require (" copilot-lsp.util" ).hl_text_to_virt_lines (lines_insertion .text , vim .bo [bufnr ].filetype )
148
+ vim .api .nvim_buf_set_extmark (bufnr , ns_id , lines_insertion .line , 0 , {
116
149
virt_lines = virt_lines ,
150
+ virt_lines_above = lines_insertion .above ,
117
151
})
118
152
end
153
+ end
154
+
155
+ --- @private
156
+ --- @param bufnr integer
157
+ --- @param ns_id integer
158
+ --- @param edits copilotlsp.InlineEdit[]
159
+ function M ._display_next_suggestion (bufnr , ns_id , edits )
160
+ M .clear_suggestion (bufnr , ns_id )
161
+ if not edits or # edits == 0 then
162
+ -- vim.notify("No suggestion available", vim.log.levels.INFO)
163
+ return
164
+ end
165
+
166
+ local suggestion = edits [1 ]
167
+
168
+ local preview = M ._calculate_preview (bufnr , suggestion )
169
+ M ._display_preview (bufnr , ns_id , preview )
119
170
120
171
vim .b [bufnr ].nes_state = suggestion
121
172
0 commit comments