Skip to content

Commit 3e288cd

Browse files
authored
refactor(ui): use virt_lines to display added lines (#11)
1 parent e4b9361 commit 3e288cd

File tree

7 files changed

+190
-74
lines changed

7 files changed

+190
-74
lines changed

lua/copilot-lsp/nes/ui.lua

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
local M = {}
22

33
---@param bufnr integer
4-
---@param suggestion_ui nes.EditSuggestionUI
54
---@param ns_id integer
6-
local function _dismiss_suggestion_ui(bufnr, suggestion_ui, ns_id)
7-
pcall(vim.api.nvim_win_close, suggestion_ui.preview_winnr, true)
5+
local function _dismiss_suggestion(bufnr, ns_id)
86
pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns_id, 0, -1)
97
end
108

@@ -19,7 +17,7 @@ function M.clear_suggestion(bufnr, ns_id)
1917
return
2018
end
2119

22-
_dismiss_suggestion_ui(bufnr, state.ui, ns_id)
20+
_dismiss_suggestion(bufnr, ns_id)
2321
vim.b[bufnr].nes_state = nil
2422
end
2523

@@ -71,21 +69,13 @@ function M._calculate_lines(suggestion)
7169
virt_lines_count = added_lines_count,
7270
}
7371

74-
-- Calculate positions for floating window
75-
---@type nes.FloatWin
76-
local float_win = {
77-
height = #added_lines,
78-
row = suggestion.range["end"].line + deleted_lines_count + (suggestion.range["end"].character ~= 0 and 1 or 0),
79-
}
80-
8172
return {
8273
deleted_lines_count = deleted_lines_count,
8374
added_lines = added_lines,
8475
added_lines_count = added_lines_count,
8576
same_line = same_line,
8677
delete_extmark = delete_extmark,
8778
virt_lines_extmark = virt_lines_extmark,
88-
float_win = float_win,
8979
}
9080
end
9181

@@ -106,7 +96,6 @@ function M._display_next_suggestion(edits, ns_id)
10696
local win_id = vim.fn.win_findbuf(bufnr)[1]
10797
local suggestion = edits[1]
10898

109-
local ui = {}
11099
local lines = M._calculate_lines(suggestion)
111100

112101
if lines.deleted_lines_count > 0 then
@@ -117,45 +106,14 @@ function M._display_next_suggestion(edits, ns_id)
117106
})
118107
end
119108
if lines.added_lines_count > 0 then
120-
-- Create space for float
121-
local virt_lines = {}
122-
for _ = 1, lines.virt_lines_extmark.virt_lines_count do
123-
table.insert(virt_lines, {
124-
{ "", "Normal" },
125-
})
126-
end
109+
local text = trim_end(edits[1].text)
110+
local virt_lines = require("copilot-lsp.util").hl_text_to_virt_lines(text, vim.bo[bufnr].filetype)
111+
127112
vim.api.nvim_buf_set_extmark(bufnr, ns_id, lines.virt_lines_extmark.row, 0, {
128113
virt_lines = virt_lines,
129114
})
130-
131-
local preview_bufnr = vim.api.nvim_create_buf(false, true)
132-
vim.api.nvim_buf_set_lines(preview_bufnr, 0, -1, false, lines.added_lines)
133-
vim.bo[preview_bufnr].modifiable = false
134-
vim.bo[preview_bufnr].buflisted = false
135-
vim.bo[preview_bufnr].bufhidden = "wipe"
136-
vim.bo[preview_bufnr].filetype = vim.bo[bufnr].filetype
137-
138-
local cursor = vim.api.nvim_win_get_cursor(win_id)
139-
local win_width = vim.api.nvim_win_get_width(win_id)
140-
local offset = vim.fn.getwininfo(win_id)[1].textoff
141-
local preview_winnr = vim.api.nvim_open_win(preview_bufnr, false, {
142-
relative = "cursor",
143-
width = win_width - offset,
144-
height = lines.float_win.height,
145-
row = lines.float_win.row - cursor[1],
146-
col = 0,
147-
style = "minimal",
148-
border = "none",
149-
})
150-
vim.wo[preview_winnr].number = false
151-
vim.wo[preview_winnr].winhighlight = "Normal:NesAdd"
152-
vim.wo[preview_winnr].winblend = 0
153-
154-
ui.preview_winnr = preview_winnr
155115
end
156116

157-
suggestion.ui = ui
158-
159117
vim.b[bufnr].nes_state = suggestion
160118

161119
vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {

lua/copilot-lsp/types.lua

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
---@field text string
55
---@field newText string
66
---@field textDocument lsp.VersionedTextDocumentIdentifier
7-
---@field ui nes.EditSuggestionUI?
87

98
---@class copilotlsp.copilotInlineEditResponse
109
---@field edits copilotlsp.InlineEdit[]
@@ -22,11 +21,6 @@
2221
---@field row number
2322
---@field virt_lines_count number
2423

25-
---@class nes.FloatWin
26-
--- Defines dimensions and position for the floating window.
27-
---@field height number
28-
---@field row number
29-
3024
---@class nes.LineCalculationResult
3125
--- The result of calculating lines for inline suggestion UI.
3226
---@field deleted_lines_count number
@@ -35,4 +29,3 @@
3529
---@field same_line number
3630
---@field delete_extmark nes.DeleteExtmark
3731
---@field virt_lines_extmark nes.VirtLinesExtmark
38-
---@field float_win nes.FloatWin

lua/copilot-lsp/util.lua

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,185 @@ function M.debounce(fn, delay)
2222
end
2323
end
2424

25+
---@private
26+
---@class Capture
27+
---@field hl string|string[]
28+
---range(0-based)
29+
---@field start_row integer
30+
---@field start_col integer
31+
---@field end_row integer
32+
---@field end_col integer
33+
34+
---@param text string
35+
---@param lang string
36+
---@return Capture[]?
37+
local function parse_text(text, lang)
38+
local ok, trees = pcall(vim.treesitter.get_string_parser, text, lang)
39+
if not ok then
40+
return
41+
end
42+
trees:parse(true)
43+
44+
local captures = {}
45+
46+
trees:for_each_tree(function(tree, _ltree)
47+
local hl_query = vim.treesitter.query.get(lang, "highlights")
48+
if not hl_query then
49+
return
50+
end
51+
52+
local iter = hl_query:iter_captures(tree:root(), text)
53+
vim.iter(iter):each(function(id, node)
54+
local name = hl_query.captures[id]
55+
local hl = "Normal"
56+
if not vim.startswith(name, "_") then
57+
hl = "@" .. name .. "." .. lang
58+
end
59+
local start_row, start_col, end_row, end_col = node:range()
60+
61+
-- Ignore zero-width captures if they cause issues (sometimes happen at EOF)
62+
if start_row == end_row and start_col == end_col then
63+
return
64+
end
65+
66+
table.insert(captures, {
67+
hl = hl,
68+
start_row = start_row,
69+
start_col = start_col,
70+
end_row = end_row,
71+
end_col = end_col,
72+
})
73+
end)
74+
end)
75+
return captures
76+
end
77+
78+
local function merge_captures(captures)
79+
table.sort(captures, function(a, b)
80+
if a.start_row == b.start_row then
81+
return a.start_col < b.start_col
82+
end
83+
return a.start_row < b.start_row
84+
end)
85+
local merged_captures = {}
86+
for i = 2, #captures do
87+
local prev = captures[i - 1]
88+
local curr = captures[i]
89+
if
90+
prev.start_row == curr.start_row
91+
and prev.start_col == curr.start_col
92+
and prev.end_row == curr.end_row
93+
and prev.end_col == curr.end_col
94+
then
95+
local prev_hl = type(prev.hl) == "table" and prev.hl or { prev.hl }
96+
local curr_hl = type(curr.hl) == "table" and curr.hl or { curr.hl }
97+
---@diagnostic disable-next-line: param-type-mismatch
98+
vim.list_extend(prev_hl, curr_hl)
99+
curr.hl = prev_hl
100+
else
101+
table.insert(merged_captures, prev)
102+
end
103+
end
104+
table.insert(merged_captures, captures[#captures])
105+
106+
return merged_captures
107+
end
108+
109+
function M.hl_text_to_virt_lines(text, lang)
110+
local lines = vim.split(text, "\n")
111+
local normal_hl = "Normal"
112+
local bg_hl = "NesAdd"
113+
114+
local function hl_chunk(chunk, hl)
115+
if not hl then
116+
return { chunk, { normal_hl, bg_hl } }
117+
end
118+
if type(hl) == "string" then
119+
return { chunk, { hl, bg_hl } }
120+
end
121+
hl = vim.deepcopy(hl)
122+
table.insert(hl, bg_hl)
123+
return { chunk, hl }
124+
end
125+
126+
local captures = parse_text(text, lang)
127+
if not captures or #captures == 0 then
128+
return vim.iter(lines)
129+
:map(function(line)
130+
return { hl_chunk(line) }
131+
end)
132+
:totable()
133+
end
134+
135+
captures = merge_captures(captures)
136+
137+
local virt_lines = {}
138+
139+
local curr_row = 0
140+
local curr_col = 0
141+
local curr_virt_line = {}
142+
143+
vim.iter(captures):each(function(cap)
144+
-- skip if the capture is before the current position
145+
if cap.end_row < curr_row or (cap.end_row == curr_row and cap.end_col < curr_col) then
146+
return
147+
end
148+
149+
if cap.start_row > curr_row then
150+
-- add the rest of the line
151+
local chunk_text = string.sub(lines[curr_row + 1], curr_col + 1)
152+
table.insert(curr_virt_line, hl_chunk(chunk_text))
153+
table.insert(virt_lines, curr_virt_line)
154+
155+
for i = curr_row + 1, cap.start_row - 1 do
156+
local line_text = lines[i + 1]
157+
table.insert(virt_lines, { hl_chunk(line_text) })
158+
end
159+
160+
curr_row = cap.start_row
161+
curr_col = 0
162+
curr_virt_line = {}
163+
end
164+
165+
assert(curr_row == cap.start_row, "Unexpected start row")
166+
167+
if cap.start_col > curr_col then
168+
local chunk_text = string.sub(lines[curr_row + 1], curr_col + 1, cap.start_col)
169+
table.insert(curr_virt_line, hl_chunk(chunk_text))
170+
curr_col = cap.start_col
171+
end
172+
173+
assert(curr_col == cap.start_col, "Unexpected start column")
174+
175+
if cap.end_row > curr_row then
176+
local chunk_text = string.sub(lines[curr_row + 1], curr_col + 1)
177+
table.insert(curr_virt_line, hl_chunk(chunk_text, cap.hl))
178+
table.insert(virt_lines, curr_virt_line)
179+
180+
for i in curr_row + 1, cap.end_row - 1 do
181+
local line_text = lines[i + 1]
182+
table.insert(virt_lines, { hl_chunk(line_text, cap.hl) })
183+
end
184+
185+
curr_row = cap.end_row
186+
curr_col = 0
187+
curr_virt_line = {}
188+
end
189+
190+
assert(curr_row == cap.end_row, "Unexpected end row")
191+
192+
if cap.end_col > curr_col then
193+
local chunk_text = string.sub(lines[curr_row + 1], curr_col + 1, cap.end_col)
194+
table.insert(curr_virt_line, hl_chunk(chunk_text, cap.hl))
195+
curr_col = cap.end_col
196+
end
197+
end)
198+
199+
if #curr_virt_line > 0 then
200+
table.insert(virt_lines, curr_virt_line)
201+
end
202+
203+
return virt_lines
204+
end
205+
25206
return M

tests/nes/test_ui.lua

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ T["diff placement calculations"] = MiniTest.new_set({
3535
row = 0,
3636
},
3737
deleted_lines_count = 1,
38-
float_win = {
39-
height = 1,
40-
row = 2,
41-
},
4238
same_line = 1,
4339
virt_lines_extmark = {
4440
row = 0,
@@ -75,10 +71,6 @@ T["diff placement calculations"] = MiniTest.new_set({
7571
row = 0,
7672
},
7773
deleted_lines_count = 1,
78-
float_win = {
79-
height = 1,
80-
row = 2,
81-
},
8274
same_line = 0,
8375
virt_lines_extmark = {
8476
row = 0,
@@ -115,10 +107,6 @@ T["diff placement calculations"] = MiniTest.new_set({
115107
row = 0,
116108
},
117109
deleted_lines_count = 1,
118-
float_win = {
119-
height = 2,
120-
row = 2,
121-
},
122110
same_line = 0,
123111
virt_lines_extmark = {
124112
row = 0,
@@ -149,10 +137,6 @@ T["diff placement calculations"] = MiniTest.new_set({
149137
end_row = 2,
150138
row = 2,
151139
},
152-
float_win = {
153-
height = 1,
154-
row = 3,
155-
},
156140
same_line = 1,
157141
virt_lines_extmark = {
158142
row = 1,

tests/screenshots/tests-nes-test_nes.lua---nes---add-only-edit-002

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
--|---------|---------|---------|---------|---------|---------|---------|---------|
2828
01|00000000000000000000000000000000000000000000000000000000000000000000000000000000
2929
02|00000000000000000000000000000000000000000000000000000000000000000000000000000000
30-
03|11111111111111111111111111111111111111111111111111111111111111111111111111111111
30+
03|11111100000000000000000000000000000000000000000000000000000000000000000000000000
3131
04|00000000000000000000000000000000000000000000000000000000000000000000000000000000
3232
05|22222222222222222222222222222222222222222222222222222222222222222222222222222222
3333
06|22222222222222222222222222222222222222222222222222222222222222222222222222222222

tests/screenshots/tests-nes-test_nes.lua---nes---multi-line-edit-002

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
--|---------|---------|---------|---------|---------|---------|---------|---------|
2828
01|00000000111111111111111111111111111111111111111111111111111111111111111111111111
2929
02|00000000111111111111111111111111111111111111111111111111111111111111111111111111
30-
03|22222222222222222222222222222222222222222222222222222222222222222222222222222222
31-
04|22222222222222222222222222222222222222222222222222222222222222222222222222222222
30+
03|22222222222211111111111111111111111111111111111111111111111111111111111111111111
31+
04|22222222222211111111111111111111111111111111111111111111111111111111111111111111
3232
05|11111111111111111111111111111111111111111111111111111111111111111111111111111111
3333
06|33333333333333333333333333333333333333333333333333333333333333333333333333333333
3434
07|33333333333333333333333333333333333333333333333333333333333333333333333333333333

tests/screenshots/tests-nes-test_nes.lua---nes---same-line-edit-002

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
--|---------|---------|---------|---------|---------|---------|---------|---------|
2828
01|00011111111111111111111111111111111111111111111111111111111111111111111111111111
29-
02|22222222222222222222222222222222222222222222222222222222222222222222222222222222
29+
02|22211111111111111111111111111111111111111111111111111111111111111111111111111111
3030
03|11111111111111111111111111111111111111111111111111111111111111111111111111111111
3131
04|11111111111111111111111111111111111111111111111111111111111111111111111111111111
3232
05|33333333333333333333333333333333333333333333333333333333333333333333333333333333

0 commit comments

Comments
 (0)