(renderer) invalidate cache on buffer change
All checks were successful
NeoVim tests / code-quality (push) Successful in 1m32s

This commit is contained in:
Jonathan Apodaca 2025-07-11 16:53:17 -06:00
parent 3682c07b3e
commit 23708652e5
4 changed files with 34 additions and 30 deletions

View File

@ -10,10 +10,10 @@
-- change on the underlying filesystem. -- change on the underlying filesystem.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
--- @alias FsDir { kind: 'dir'; path: string; expanded: boolean; children: FsNode[] } --- @alias u.examples.FsDir { kind: 'dir'; path: string; expanded: boolean; children: u.examples.FsNode[] }
--- @alias FsFile { kind: 'file'; path: string } --- @alias u.examples.FsFile { kind: 'file'; path: string }
--- @alias FsNode FsDir | FsFile --- @alias u.examples.FsNode u.examples.FsDir | u.examples.FsFile
--- @alias ShowOpts { root_path?: string, width?: number, focus_path?: string } --- @alias u.examples.ShowOpts { root_path?: string, width?: number, focus_path?: string }
local Buffer = require 'u.buffer' local Buffer = require 'u.buffer'
local Renderer = require('u.renderer').Renderer local Renderer = require('u.renderer').Renderer
@ -58,13 +58,13 @@ function H.relative(path, base)
end end
--- @param root_path string --- @param root_path string
--- @return { tree: FsDir; path_to_node: table<string, FsNode> } --- @return { tree: u.examples.FsDir; path_to_node: table<string, u.examples.FsNode> }
function H.get_tree_inf(root_path) function H.get_tree_inf(root_path)
logger:info { 'get_tree_inf', root_path } logger:info { 'get_tree_inf', root_path }
--- @type table<string, FsNode> --- @type table<string, u.examples.FsNode>
local path_to_node = {} local path_to_node = {}
--- @type FsDir --- @type u.examples.FsDir
local tree = { local tree = {
kind = 'dir', kind = 'dir',
path = H.normalize(root_path or '.'), path = H.normalize(root_path or '.'),
@ -77,8 +77,8 @@ function H.get_tree_inf(root_path)
return { tree = tree, path_to_node = path_to_node } return { tree = tree, path_to_node = path_to_node }
end end
--- @param tree FsDir --- @param tree u.examples.FsDir
--- @param path_to_node table<string, FsNode> --- @param path_to_node table<string, u.examples.FsNode>
function H.populate_dir_children(tree, path_to_node) function H.populate_dir_children(tree, path_to_node)
tree.children = {} tree.children = {}
@ -135,7 +135,7 @@ local function _render_in_buffer(opts)
local parts = H.split_path(H.relative(focused_path, tree_inf.tree.path)) local parts = H.split_path(H.relative(focused_path, tree_inf.tree.path))
local path_to_node = tree_inf.path_to_node local path_to_node = tree_inf.path_to_node
--- @param node FsDir --- @param node u.examples.FsDir
--- @param child_names string[] --- @param child_names string[]
local function expand_to(node, child_names) local function expand_to(node, child_names)
if #child_names == 0 then return end if #child_names == 0 then return end
@ -310,7 +310,7 @@ local function _render_in_buffer(opts)
-- --
local renderer = Renderer.new(opts.bufnr) local renderer = Renderer.new(opts.bufnr)
tracker.create_effect(function() tracker.create_effect(function()
--- @type { tree: FsDir; path_to_node: table<string, FsNode> } --- @type { tree: u.examples.FsDir; path_to_node: table<string, u.examples.FsNode> }
local tree_inf = s_tree_inf:get() local tree_inf = s_tree_inf:get()
local tree = tree_inf.tree local tree = tree_inf.tree
@ -329,7 +329,7 @@ local function _render_in_buffer(opts)
--- Since the filesystem is a recursive tree of nodes, we need to --- Since the filesystem is a recursive tree of nodes, we need to
--- recursively render each node. This function does just that: --- recursively render each node. This function does just that:
--- @param node FsNode --- @param node u.examples.FsNode
--- @param level number --- @param level number
local function render_node(node, level) local function render_node(node, level)
local name = vim.fs.basename(node.path) local name = vim.fs.basename(node.path)
@ -414,7 +414,7 @@ end
local current_inf = nil local current_inf = nil
--- Show the filetree: --- Show the filetree:
--- @param opts? ShowOpts --- @param opts? u.examples.ShowOpts
function M.show(opts) function M.show(opts)
if current_inf ~= nil then return current_inf.controller end if current_inf ~= nil then return current_inf.controller end
opts = opts or {} opts = opts or {}
@ -456,7 +456,7 @@ function M.hide()
end end
--- Toggle the filetree: --- Toggle the filetree:
--- @param opts? ShowOpts --- @param opts? u.examples.ShowOpts
function M.toggle(opts) function M.toggle(opts)
if current_inf == nil then if current_inf == nil then
M.show(opts) M.show(opts)

View File

@ -14,7 +14,7 @@ local ICONS = {
} }
local DEFAULT_ICON = { text = '', group = 'DiagnosticSignOk' } local DEFAULT_ICON = { text = '', group = 'DiagnosticSignOk' }
--- @alias Notification { --- @alias u.examples.Notification {
--- kind: number; --- kind: number;
--- id: number; --- id: number;
--- text: string; --- text: string;
@ -30,7 +30,7 @@ local s_notifications = s_notifications_raw:debounce(50)
-- Render effect: -- Render effect:
tracker.create_effect(function() tracker.create_effect(function()
--- @type Notification[] --- @type u.examples.Notification[]
local notifs = s_notifications:get() local notifs = s_notifications:get()
if #notifs == 0 then if #notifs == 0 then
@ -99,14 +99,14 @@ local function my_notify(msg, level, opts)
local id = math.random(math.huge) local id = math.random(math.huge)
--- @param notifs Notification[] --- @param notifs u.examples.Notification[]
s_notifications_raw:schedule_update(function(notifs) s_notifications_raw:schedule_update(function(notifs)
table.insert(notifs, { kind = level, id = id, text = msg }) table.insert(notifs, { kind = level, id = id, text = msg })
return notifs return notifs
end) end)
vim.defer_fn(function() vim.defer_fn(function()
--- @param notifs Notification[] --- @param notifs u.examples.Notification[]
s_notifications_raw:schedule_update(function(notifs) s_notifications_raw:schedule_update(function(notifs)
for i, notif in ipairs(notifs) do for i, notif in ipairs(notifs) do
if notif.id == id then if notif.id == id then

View File

@ -44,7 +44,7 @@ local function shallow_copy_arr(arr) return vim.iter(arr):totable() end
-- shortest portion of this function. -- shortest portion of this function.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
--- @alias SelectController { --- @alias u.examples.SelectController {
--- get_items: fun(): T[]; --- get_items: fun(): T[];
--- set_items: fun(items: T[]); --- set_items: fun(items: T[]);
--- set_filter_text: fun(filter_text: string); --- set_filter_text: fun(filter_text: string);
@ -53,17 +53,17 @@ local function shallow_copy_arr(arr) return vim.iter(arr):totable() end
--- set_selected_indices: fun(indicies: number[], ephemeral?: boolean); --- set_selected_indices: fun(indicies: number[], ephemeral?: boolean);
--- close: fun(); --- close: fun();
--- } --- }
--- @alias SelectOpts<T> { --- @alias u.examples.SelectOpts<T> {
--- items: `T`[]; --- items: `T`[];
--- multi?: boolean; --- multi?: boolean;
--- format_item?: fun(item: T): Tree; --- format_item?: fun(item: T): u.renderer.Tree;
--- on_finish?: fun(items: T[], indicies: number[]); --- on_finish?: fun(items: T[], indicies: number[]);
--- on_selection_changed?: fun(items: T[], indicies: number[]); --- on_selection_changed?: fun(items: T[], indicies: number[]);
--- mappings?: table<string, fun(select: SelectController)>; --- mappings?: table<string, fun(select: u.examples.SelectController)>;
--- } --- }
--- @generic T --- @generic T
--- @param opts SelectOpts<T> --- @param opts u.examples.SelectOpts<T>
function M.create_picker(opts) -- {{{ function M.create_picker(opts) -- {{{
local is_in_insert_mode = vim.api.nvim_get_mode().mode:sub(1, 1) == 'i' local is_in_insert_mode = vim.api.nvim_get_mode().mode:sub(1, 1) == 'i'
local stopinsert = not is_in_insert_mode local stopinsert = not is_in_insert_mode
@ -557,7 +557,7 @@ function M.create_picker(opts) -- {{{
return safe_run(function() H.finish(true) end) return safe_run(function() H.finish(true) end)
end end
return controller --[[@as SelectController]] return controller --[[@as u.examples.SelectController]]
end -- }}} end -- }}}
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -373,6 +373,10 @@ function Renderer:_expr_map_callback(mode, lhs) -- {{{
end -- }}} end -- }}}
function Renderer:_on_text_changed() function Renderer:_on_text_changed()
-- Reset changedtick, so that the reconciler knows to refresh its cached
-- buffer-content before computing the diff:
self.changedtick = 0
--- @type integer, integer --- @type integer, integer
local l, c = unpack(vim.api.nvim_win_get_cursor(0)) local l, c = unpack(vim.api.nvim_win_get_cursor(0))
l = l - 1 -- make it actually 0-based l = l - 1 -- make it actually 0-based
@ -574,13 +578,13 @@ function TreeBuilder:tree() return self.nodes end
-- Levenshtein utility {{{ -- Levenshtein utility {{{
-- luacheck: ignore -- luacheck: ignore
--- @alias LevenshteinChange<T> ({ kind: 'add'; item: T; index: number; } | { kind: 'delete'; item: T; index: number; } | { kind: 'change'; from: T; to: T; index: number; }) --- @alias u.renderer.LevenshteinChange<T> ({ kind: 'add'; item: T; index: number; } | { kind: 'delete'; item: T; index: number; } | { kind: 'change'; from: T; to: T; index: number; })
--- @private --- @private
--- @generic T --- @generic T
--- @param x `T`[] --- @param x `T`[]
--- @param y T[] --- @param y T[]
--- @param cost? { of_delete?: fun(x: T): number; of_add?: fun(x: T): number; of_change?: fun(x: T, y: T): number; } --- @param cost? { of_delete?: fun(x: T): number; of_add?: fun(x: T): number; of_change?: fun(x: T, y: T): number; }
--- @return LevenshteinChange<T>[] The changes, from last (greatest index) to first (smallest index). --- @return u.renderer.LevenshteinChange<T>[] The changes, from last (greatest index) to first (smallest index).
function H.levenshtein(x, y, cost) function H.levenshtein(x, y, cost)
-- At the moment, this whole `cost` plumbing is not used. Deletes have the -- At the moment, this whole `cost` plumbing is not used. Deletes have the
-- same cost as Adds or Changes. I can imagine a future, however, where -- same cost as Adds or Changes. I can imagine a future, however, where
@ -631,7 +635,7 @@ function H.levenshtein(x, y, cost)
-- Backtrack to find the changes -- Backtrack to find the changes
local i = m local i = m
local j = n local j = n
--- @type LevenshteinChange[] --- @type u.renderer.LevenshteinChange[]
local changes = {} local changes = {}
while i > 0 or j > 0 do while i > 0 or j > 0 do
@ -648,7 +652,7 @@ function H.levenshtein(x, y, cost)
if is_first_min(cost_of_change, cost_of_add, cost_of_delete) then if is_first_min(cost_of_change, cost_of_add, cost_of_delete) then
-- potential change -- potential change
if x[i] ~= y[j] then if x[i] ~= y[j] then
--- @type LevenshteinChange --- @type u.renderer.LevenshteinChange
local change = { kind = 'change', from = x[i], index = i, to = y[j] } local change = { kind = 'change', from = x[i], index = i, to = y[j] }
table.insert(changes, change) table.insert(changes, change)
end end
@ -656,13 +660,13 @@ function H.levenshtein(x, y, cost)
j = j - 1 j = j - 1
elseif is_first_min(cost_of_add, cost_of_change, cost_of_delete) then elseif is_first_min(cost_of_add, cost_of_change, cost_of_delete) then
-- addition -- addition
--- @type LevenshteinChange --- @type u.renderer.LevenshteinChange
local change = { kind = 'add', item = y[j], index = i + 1 } local change = { kind = 'add', item = y[j], index = i + 1 }
table.insert(changes, change) table.insert(changes, change)
j = j - 1 j = j - 1
elseif is_first_min(cost_of_delete, cost_of_change, cost_of_add) then elseif is_first_min(cost_of_delete, cost_of_change, cost_of_add) then
-- deletion -- deletion
--- @type LevenshteinChange --- @type u.renderer.LevenshteinChange
local change = { kind = 'delete', item = x[i], index = i } local change = { kind = 'delete', item = x[i], index = i }
table.insert(changes, change) table.insert(changes, change)
i = i - 1 i = i - 1