canola.nvim/lua/canola/mutator/trie.lua
Barrett Ruth 0d3088f57e
refactor: rename oil to canola across entire codebase (#70)
Problem: the codebase still used the upstream \`oil\` naming everywhere —
URL schemes, the \`:Oil\` command, highlight groups, user events, module
paths, filetypes, buffer/window variables, LuaCATS type annotations,
vimdoc help tags, syntax groups, and internal identifiers.

Solution: mechanical rename of every reference. URL schemes now use
\`canola://\` (plus \`canola-ssh://\`, \`canola-s3://\`, \`canola-sss://\`,
\`canola-trash://\`, \`canola-test://\`). The \`:Canola\` command replaces
\`:Oil\`. All highlight groups, user events, augroups, namespaces,
filetypes, require paths, type annotations, help tags, and identifiers
follow suit. The \`upstream\` remote to \`stevearc/oil.nvim\` has been
removed and the \`vim.g.oil\` deprecation shim dropped.
2026-03-05 14:50:10 -05:00

160 lines
4.1 KiB
Lua

local util = require('canola.util')
---@class (exact) canola.Trie
---@field new fun(): canola.Trie
---@field private root table
local Trie = {}
---@return canola.Trie
Trie.new = function()
---@type canola.Trie
return setmetatable({
root = { values = {}, children = {} },
}, {
__index = Trie,
})
end
---@param url string
---@return string[]
function Trie:_url_to_path_pieces(url)
local scheme, path = util.parse_url(url)
assert(path)
local pieces = vim.split(path, '/')
table.insert(pieces, 1, scheme)
return pieces
end
---@param url string
---@param value any
function Trie:insert_action(url, value)
local pieces = self:_url_to_path_pieces(url)
self:insert(pieces, value)
end
---@param url string
---@param value any
function Trie:remove_action(url, value)
local pieces = self:_url_to_path_pieces(url)
self:remove(pieces, value)
end
---@param path_pieces string[]
---@param value any
function Trie:insert(path_pieces, value)
local current = self.root
for _, piece in ipairs(path_pieces) do
local next_container = current.children[piece]
if not next_container then
next_container = { values = {}, children = {} }
current.children[piece] = next_container
end
current = next_container
end
table.insert(current.values, value)
end
---@param path_pieces string[]
---@param value any
function Trie:remove(path_pieces, value)
local current = self.root
for _, piece in ipairs(path_pieces) do
local next_container = current.children[piece]
if not next_container then
next_container = { values = {}, children = {} }
current.children[piece] = next_container
end
current = next_container
end
for i, v in ipairs(current.values) do
if v == value then
table.remove(current.values, i)
-- if vim.tbl_isempty(current.values) and vim.tbl_isempty(current.children) then
-- TODO remove container from trie
-- end
return
end
end
error('Value not present in trie: ' .. vim.inspect(value))
end
---Add the first action that affects a parent path of the url
---@param url string
---@param ret canola.InternalEntry[]
function Trie:accum_first_parents_of(url, ret)
local pieces = self:_url_to_path_pieces(url)
local containers = { self.root }
for _, piece in ipairs(pieces) do
local next_container = containers[#containers].children[piece]
table.insert(containers, next_container)
end
table.remove(containers)
while not vim.tbl_isempty(containers) do
local container = containers[#containers]
if not vim.tbl_isempty(container.values) then
vim.list_extend(ret, container.values)
break
end
table.remove(containers)
end
end
---Do a depth-first-search and add all children matching the filter
function Trie:_dfs(container, ret, filter)
if filter then
for _, action in ipairs(container.values) do
if filter(action) then
table.insert(ret, action)
end
end
else
vim.list_extend(ret, container.values)
end
for _, child in ipairs(container.children) do
self:_dfs(child, ret)
end
end
---Add all actions affecting children of the url
---@param url string
---@param ret canola.InternalEntry[]
---@param filter nil|fun(entry: canola.Action): boolean
function Trie:accum_children_of(url, ret, filter)
local pieces = self:_url_to_path_pieces(url)
local current = self.root
for _, piece in ipairs(pieces) do
current = current.children[piece]
if not current then
return
end
end
if current then
for _, child in pairs(current.children) do
self:_dfs(child, ret, filter)
end
end
end
---Add all actions at a specific path
---@param url string
---@param ret canola.InternalEntry[]
---@param filter? fun(entry: canola.Action): boolean
function Trie:accum_actions_at(url, ret, filter)
local pieces = self:_url_to_path_pieces(url)
local current = self.root
for _, piece in ipairs(pieces) do
current = current.children[piece]
if not current then
return
end
end
if current then
for _, action in ipairs(current.values) do
if not filter or filter(action) then
table.insert(ret, action)
end
end
end
end
return Trie