optimize: live diff performance (#1297)
This commit is contained in:
@@ -899,8 +899,6 @@ function Sidebar:apply(current_cursor)
|
|||||||
|
|
||||||
local function clear_highlights() api.nvim_buf_clear_namespace(bufnr, ns_id, 0, -1) end
|
local function clear_highlights() api.nvim_buf_clear_namespace(bufnr, ns_id, 0, -1) end
|
||||||
|
|
||||||
local last_processed_line = 0
|
|
||||||
|
|
||||||
-- Create loading indicator float window
|
-- Create loading indicator float window
|
||||||
local loading_buf = api.nvim_create_buf(false, true)
|
local loading_buf = api.nvim_create_buf(false, true)
|
||||||
local loading_win = nil
|
local loading_win = nil
|
||||||
@@ -962,6 +960,30 @@ function Sidebar:apply(current_cursor)
|
|||||||
clear_highlights()
|
clear_highlights()
|
||||||
create_loading_window()
|
create_loading_window()
|
||||||
|
|
||||||
|
local last_processed_line = 0
|
||||||
|
local last_orig_diff_end_line = 1
|
||||||
|
local last_resp_diff_end_line = 1
|
||||||
|
local cleaned = false
|
||||||
|
local prev_patch = {}
|
||||||
|
|
||||||
|
local function get_stable_patch(patch)
|
||||||
|
local new_patch = {}
|
||||||
|
for _, hunk in ipairs(patch) do
|
||||||
|
local start_a, count_a, start_b, count_b = unpack(hunk)
|
||||||
|
start_a = start_a + last_orig_diff_end_line - 1
|
||||||
|
start_b = start_b + last_resp_diff_end_line - 1
|
||||||
|
local has = vim.iter(prev_patch):find(function(hunk_)
|
||||||
|
local start_a_, count_a_, start_b_, count_b_ = unpack(hunk_)
|
||||||
|
return start_a == start_a_ and start_b == start_b_ and count_a == count_a_ and count_b == count_b_
|
||||||
|
end)
|
||||||
|
if has ~= nil then table.insert(new_patch, hunk) end
|
||||||
|
end
|
||||||
|
return new_patch
|
||||||
|
end
|
||||||
|
|
||||||
|
local extmark_id_map = {}
|
||||||
|
local virt_lines_map = {}
|
||||||
|
|
||||||
Llm.stream({
|
Llm.stream({
|
||||||
ask = true,
|
ask = true,
|
||||||
provider = cursor_applying_provider,
|
provider = cursor_applying_provider,
|
||||||
@@ -975,40 +997,50 @@ function Sidebar:apply(current_cursor)
|
|||||||
|
|
||||||
resp_content = resp_content .. chunk
|
resp_content = resp_content .. chunk
|
||||||
|
|
||||||
local clean_content = resp_content:gsub("<updated%-code>\n*", ""):gsub("</updated%-code>\n*", "")
|
if not cleaned then
|
||||||
clean_content = clean_content:gsub(".*```%w+\n", ""):gsub("\n```\n.*", "")
|
resp_content = resp_content:gsub("<updated%-code>\n*", ""):gsub("</updated%-code>\n*", "")
|
||||||
local resp_lines = vim.split(clean_content, "\n")
|
resp_content = resp_content:gsub(".*```%w+\n", ""):gsub("\n```\n.*", "")
|
||||||
|
end
|
||||||
|
|
||||||
|
local resp_lines = vim.split(resp_content, "\n")
|
||||||
|
|
||||||
local complete_lines_count = #resp_lines - 1
|
local complete_lines_count = #resp_lines - 1
|
||||||
|
if complete_lines_count > 2 then cleaned = true end
|
||||||
|
|
||||||
if complete_lines_count <= last_processed_line then return end
|
if complete_lines_count <= last_processed_line then return end
|
||||||
|
|
||||||
local original_lines = vim.list_slice(original_code_lines, 1, complete_lines_count)
|
local original_lines_to_process =
|
||||||
local resp_lines_to_process = vim.list_slice(resp_lines, 1, complete_lines_count)
|
vim.list_slice(original_code_lines, last_orig_diff_end_line, complete_lines_count)
|
||||||
|
local resp_lines_to_process = vim.list_slice(resp_lines, last_resp_diff_end_line, complete_lines_count)
|
||||||
|
|
||||||
local resp_lines_content = table.concat(resp_lines_to_process, "\n")
|
local resp_lines_content = table.concat(resp_lines_to_process, "\n")
|
||||||
local original_lines_content = table.concat(original_lines, "\n")
|
local original_lines_content = table.concat(original_lines_to_process, "\n")
|
||||||
|
|
||||||
---@diagnostic disable-next-line: assign-type-mismatch, missing-fields
|
---@diagnostic disable-next-line: assign-type-mismatch, missing-fields
|
||||||
local patch = vim.diff(original_lines_content, resp_lines_content, { ---@type integer[][]
|
local patch = vim.diff(original_lines_content, resp_lines_content, { ---@type integer[][]
|
||||||
algorithm = "minimal",
|
algorithm = "histogram",
|
||||||
result_type = "indices",
|
result_type = "indices",
|
||||||
ctxlen = vim.o.scrolloff,
|
ctxlen = vim.o.scrolloff,
|
||||||
})
|
})
|
||||||
|
|
||||||
clear_highlights()
|
local stable_patch = get_stable_patch(patch)
|
||||||
|
|
||||||
for _, hunk in ipairs(patch) do
|
for _, hunk in ipairs(stable_patch) do
|
||||||
local start_a, count_a, start_b, count_b = unpack(hunk)
|
local start_a, count_a, start_b, count_b = unpack(hunk)
|
||||||
|
|
||||||
for i = start_a, start_a + count_a - 1 do
|
start_a = last_orig_diff_end_line + start_a - 1
|
||||||
api.nvim_buf_set_extmark(bufnr, ns_id, i - 1, 0, {
|
|
||||||
|
if count_a > 0 then
|
||||||
|
api.nvim_buf_set_extmark(bufnr, ns_id, start_a - 1, 0, {
|
||||||
hl_group = Highlights.TO_BE_DELETED_WITHOUT_STRIKETHROUGH,
|
hl_group = Highlights.TO_BE_DELETED_WITHOUT_STRIKETHROUGH,
|
||||||
hl_eol = true,
|
hl_eol = true,
|
||||||
hl_mode = "combine",
|
hl_mode = "combine",
|
||||||
end_row = i,
|
end_row = start_a + count_a - 1,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if count_b == 0 then goto continue end
|
||||||
|
|
||||||
local new_lines = vim.list_slice(resp_lines_to_process, start_b, start_b + count_b - 1)
|
local new_lines = vim.list_slice(resp_lines_to_process, start_b, start_b + count_b - 1)
|
||||||
local max_col = vim.o.columns
|
local max_col = vim.o.columns
|
||||||
local virt_lines = vim
|
local virt_lines = vim
|
||||||
@@ -1019,11 +1051,45 @@ function Sidebar:apply(current_cursor)
|
|||||||
return { { line_, Highlights.INCOMING } }
|
return { { line_, Highlights.INCOMING } }
|
||||||
end)
|
end)
|
||||||
:totable()
|
:totable()
|
||||||
api.nvim_buf_set_extmark(bufnr, ns_id, math.max(0, start_a + count_a - 2), 0, {
|
local extmark_line
|
||||||
|
if count_a > 0 then
|
||||||
|
extmark_line = math.max(0, start_a + count_a - 2)
|
||||||
|
else
|
||||||
|
extmark_line = math.max(0, start_a + count_a - 1)
|
||||||
|
end
|
||||||
|
local old_extmark_id = extmark_id_map[extmark_line]
|
||||||
|
if old_extmark_id ~= nil then
|
||||||
|
local old_virt_lines = virt_lines_map[old_extmark_id] or {}
|
||||||
|
virt_lines = vim.list_extend(old_virt_lines, virt_lines)
|
||||||
|
api.nvim_buf_del_extmark(bufnr, ns_id, old_extmark_id)
|
||||||
|
end
|
||||||
|
local extmark_id = api.nvim_buf_set_extmark(bufnr, ns_id, extmark_line, 0, {
|
||||||
virt_lines = virt_lines,
|
virt_lines = virt_lines,
|
||||||
hl_eol = true,
|
hl_eol = true,
|
||||||
hl_mode = "combine",
|
hl_mode = "combine",
|
||||||
})
|
})
|
||||||
|
extmark_id_map[extmark_line] = extmark_id
|
||||||
|
virt_lines_map[extmark_id] = virt_lines
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
|
||||||
|
prev_patch = vim
|
||||||
|
.iter(patch)
|
||||||
|
:map(function(hunk)
|
||||||
|
local start_a, count_a, start_b, count_b = unpack(hunk)
|
||||||
|
return { last_orig_diff_end_line + start_a - 1, count_a, last_resp_diff_end_line + start_b - 1, count_b }
|
||||||
|
end)
|
||||||
|
:totable()
|
||||||
|
|
||||||
|
if #stable_patch > 0 then
|
||||||
|
local start_a, count_a, start_b, count_b = unpack(stable_patch[#stable_patch])
|
||||||
|
last_orig_diff_end_line = last_orig_diff_end_line + start_a + math.max(count_a, 1) - 1
|
||||||
|
last_resp_diff_end_line = last_resp_diff_end_line + start_b + math.max(count_b, 1) - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if #patch == 0 then
|
||||||
|
last_orig_diff_end_line = complete_lines_count + 1
|
||||||
|
last_resp_diff_end_line = complete_lines_count + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
last_processed_line = complete_lines_count
|
last_processed_line = complete_lines_count
|
||||||
@@ -1035,10 +1101,10 @@ function Sidebar:apply(current_cursor)
|
|||||||
--- goto window winid
|
--- goto window winid
|
||||||
api.nvim_set_current_win(winid)
|
api.nvim_set_current_win(winid)
|
||||||
--- goto the last line
|
--- goto the last line
|
||||||
if last_processed_line > #original_code_lines then
|
if last_orig_diff_end_line > #original_code_lines then
|
||||||
api.nvim_win_set_cursor(winid, { #original_code_lines, 0 })
|
api.nvim_win_set_cursor(winid, { #original_code_lines, 0 })
|
||||||
else
|
else
|
||||||
api.nvim_win_set_cursor(winid, { last_processed_line, 0 })
|
api.nvim_win_set_cursor(winid, { last_orig_diff_end_line, 0 })
|
||||||
end
|
end
|
||||||
vim.cmd("normal! zz")
|
vim.cmd("normal! zz")
|
||||||
end,
|
end,
|
||||||
|
|||||||
Reference in New Issue
Block a user