From 5a6451a85e1f68cdb521d16d5e1595304aadfd21 Mon Sep 17 00:00:00 2001 From: Jonathan Apodaca Date: Thu, 24 Apr 2025 10:00:55 -0600 Subject: [PATCH] factor out line-patching into a separate function --- lua/u/renderer.lua | 93 ++++++++++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/lua/u/renderer.lua b/lua/u/renderer.lua index e497166..389d3f6 100644 --- a/lua/u/renderer.lua +++ b/lua/u/renderer.lua @@ -132,6 +132,64 @@ end -- }}} --- } function Renderer.markup_to_string(opts) return table.concat(Renderer.markup_to_lines(opts), '\n') end +--- @param bufnr number +--- @param old_lines string[] | nil +--- @param new_lines string[] +function Renderer.patch_lines(bufnr, old_lines, new_lines) + -- + -- Helpers: + -- + + --- @param start integer + --- @param end_ integer + --- @param strict_indexing boolean + --- @param replacement string[] + local function _set_lines(start, end_, strict_indexing, replacement) + vim.api.nvim_buf_set_lines(bufnr, start, end_, strict_indexing, replacement) + end + + --- @param start_row integer + --- @param start_col integer + --- @param end_row integer + --- @param end_col integer + --- @param replacement string[] + local function _set_text(start_row, start_col, end_row, end_col, replacement) + vim.api.nvim_buf_set_text(bufnr, start_row, start_col, end_row, end_col, replacement) + end + + -- Morph the text to the desired state: + local line_changes = + H.levenshtein(old_lines or vim.api.nvim_buf_get_lines(bufnr, 0, -1, false), new_lines) + for _, line_change in ipairs(line_changes) do + local lnum0 = line_change.index - 1 + + if line_change.kind == 'add' then + _set_lines(lnum0, lnum0, true, { line_change.item }) + elseif line_change.kind == 'change' then + -- Compute inter-line diff, and apply: + local col_changes = + H.levenshtein(vim.split(line_change.from, ''), vim.split(line_change.to, '')) + + for _, col_change in ipairs(col_changes) do + local cnum0 = col_change.index - 1 + if col_change.kind == 'add' then + _set_text(lnum0, cnum0, lnum0, cnum0, { col_change.item }) + elseif col_change.kind == 'change' then + _set_text(lnum0, cnum0, lnum0, cnum0 + 1, { col_change.to }) + elseif col_change.kind == 'delete' then + _set_text(lnum0, cnum0, lnum0, cnum0 + 1, {}) + else + -- No change + end + end + elseif line_change.kind == 'delete' then + _set_lines(lnum0, lnum0 + 1, true, {}) + else + -- No change + end + end +end + --- @param tree u.renderer.Tree function Renderer:render(tree) -- {{{ local changedtick = vim.b[self.bufnr].changedtick @@ -210,45 +268,16 @@ end --- @private function Renderer:_reconcile() -- {{{ - local line_changes = H.levenshtein(self.old.lines, self.curr.lines) - self.old = self.curr - -- -- Step 1: morph the text to the desired state: -- - for _, line_change in ipairs(line_changes) do - local lnum0 = line_change.index - 1 - - if line_change.kind == 'add' then - self:_set_lines(lnum0, lnum0, true, { line_change.item }) - elseif line_change.kind == 'change' then - -- Compute inter-line diff, and apply: - local col_changes = - H.levenshtein(vim.split(line_change.from, ''), vim.split(line_change.to, '')) - - for _, col_change in ipairs(col_changes) do - local cnum0 = col_change.index - 1 - if col_change.kind == 'add' then - self:_set_text(lnum0, cnum0, lnum0, cnum0, { col_change.item }) - elseif col_change.kind == 'change' then - self:_set_text(lnum0, cnum0, lnum0, cnum0 + 1, { col_change.to }) - elseif col_change.kind == 'delete' then - self:_set_text(lnum0, cnum0, lnum0, cnum0 + 1, {}) - else - -- No change - end - end - elseif line_change.kind == 'delete' then - self:_set_lines(lnum0, lnum0 + 1, true, {}) - else - -- No change - end - end + Renderer.patch_lines(self.bufnr, self.old.lines, self.curr.lines) self.changedtick = vim.b[self.bufnr].changedtick -- -- Step 2: reconcile extmarks: -- + -- Clear current extmarks: vim.api.nvim_buf_clear_namespace(self.bufnr, self.ns, 0, -1) -- Set current extmarks: @@ -265,6 +294,8 @@ function Renderer:_reconcile() -- {{{ }, extmark.opts) ) end + + self.old = self.curr end -- }}} --- @private