Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(type): add type annotation for defaults.lua #1333

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
014f98a
refactor(type): add type annotation for defaults.lua
pysan3 Feb 2, 2024
efcefd7
ci(lua_ls): add lua_ls actions to check type annotation
pysan3 Feb 2, 2024
fde92b3
chore: autoformat with stylua
pysan3 Feb 2, 2024
0c048b4
ci(lua_ls): force run type check on all branches for now
pysan3 Feb 2, 2024
5b8f58c
Merge branch 'add-type-annotations' of github.com:pysan3/neo-tree.nvi…
pysan3 Feb 2, 2024
f5d2207
fix(ci): fix typo
pysan3 Feb 2, 2024
913d998
ci(lua_ls): refer to types defined in the repo
pysan3 Feb 2, 2024
adee9b9
fix(type): better organized type names
pysan3 Feb 2, 2024
72776bc
refactor(type): move config types to another file
pysan3 Feb 3, 2024
ead7513
refactor(type): add types to log.lua
pysan3 Feb 3, 2024
e6236b9
refactor(type): add type for merge_config
pysan3 Feb 5, 2024
c331b7b
ci(lua_ls): add more files to check types
pysan3 Feb 5, 2024
4cfcbe2
refactor(defaults): move event_handler examples to wiki
pysan3 Feb 5, 2024
4b68620
refactor(types): add type files to library
pysan3 Feb 5, 2024
7744d57
refactor(types): ignore wrong or deprecated types
pysan3 Feb 5, 2024
e85fdcc
ci(lua_ls): remove unnecessary types to check
pysan3 Feb 5, 2024
a28b905
refactor(types): remove collections.lua from type check
pysan3 Feb 5, 2024
c4ab2c3
ci(lua_ls): make lua_ls typecheck run on PRs
pysan3 Feb 5, 2024
9ce9563
fix(type): add lacking field
pysan3 Feb 6, 2024
b42cefe
refactor(type): delete types to prevent lua_ls performance issue
pysan3 Feb 9, 2024
12a67ae
refactor(type): move component types to separate file
pysan3 Feb 9, 2024
710ddaf
refactor(type): add types to functions in common/components
pysan3 Feb 9, 2024
c1eaadb
refactor(type): capitalize all enum keys
pysan3 Feb 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor(type): add type for merge_config
  • Loading branch information
pysan3 committed Feb 5, 2024
commit e6236b9e4d623cc2a333e2ca507f4eff115b8867
3 changes: 3 additions & 0 deletions lua/neo-tree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ M.set_log_level = function(level)
log.set_level(level)
end

---Entry point for Neotree. Welcome to Neotree!
---@param config NeotreeConfig
---@param is_auto_config boolean|nil # When true, this function is called for testing. Skip changes to vim state.
M.setup = function(config, is_auto_config)
M.config = require("neo-tree.setup").merge_config(config, is_auto_config)
local netrw = require("neo-tree.setup.netrw")
Expand Down
120 changes: 72 additions & 48 deletions lua/neo-tree/collections.lua
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
local log = require("neo-tree.log")
---@class NeotreeCollections
local M = {}

local Node = {}
function Node:new(value)
---@class NeotreeListNode<T> : { prev: NeotreeListNode<T>|nil, next: NeotreeListNode<T>|nil, value: T }
M.Node = {}

---Create new NeotreeListNode of type <T>
---@generic T
---@param value T
---@return NeotreeListNode<T>
function M.Node:new(value)
local props = { prev = nil, next = nil, value = value }
setmetatable(props, self)
self.__index = self
return props
end

local LinkedList = {}
function LinkedList:new()
---@class NeotreeLinkedList<T> : { head: NeotreeListNode<T>, tail: NeotreeListNode<T>, size: integer }
M.LinkedList = {}
function M.LinkedList:new()
local props = { head = nil, tail = nil, size = 0 }
setmetatable(props, self)
self.__index = self
return props
end

function LinkedList:add_node(node)
---Add NeotreeListNode into linked list
---@generic T
---@param node NeotreeListNode<T>
---@return NeotreeListNode<T>
function M.LinkedList:add_node(node)
if self.head == nil then
self.head = node
self.tail = node
Expand All @@ -29,7 +42,10 @@ function LinkedList:add_node(node)
return node
end

function LinkedList:remove_node(node)
---Remove NeotreeListNode from a linked list
---@generic T
---@param node NeotreeListNode<T>
function M.LinkedList:remove_node(node)
if node.prev ~= nil then
node.prev.next = node.next
end
Expand All @@ -48,70 +64,77 @@ function LinkedList:remove_node(node)
node.value = nil
end

-- First in Last Out
local Queue = {}
function Queue:new()
local props = { _list = LinkedList:new() }
---Clear all nodes in the list
function M.LinkedList:clear()
local current = self.head
while current ~= nil do
local next = current.next
self:remove_node(current)
current = next
end
end

---@class (exact) NeotreeQueue<T> : { _list: NeotreeLinkedList<T> }
---@field _list NeotreeLinkedList
M.Queue = {}

function M.Queue:new()
local props = {}
props._list = M.LinkedList:new()
setmetatable(props, self)
self.__index = self
self.__index = self ---@diagnostic disable-line
return props
end

---Add an element to the end of the queue.
---@param value any The value to add.
function Queue:add(value)
self._list:add_node(Node:new(value))
---@generic T
---@param value T The value to add.
function M.Queue:add(value)
self._list:add_node(M.Node:new(value))
end

---Iterates over the entire list, running func(value) on each element.
---If func returns true, the element is removed from the list.
---@param func function The function to run on each element.
function Queue:for_each(func)
---@generic T
---@param func fun(node: T): boolean|{ handled: boolean } # The function to run on each element.
function M.Queue:for_each(func)
local node = self._list.head
while node ~= nil do
local result = func(node.value)
local node_is_next = false
if result then
if type(result) == "boolean" then
local node_to_remove = node
node = node.next
node_is_next = true
self._list:remove_node(node_to_remove)
elseif type(result) == "table" then
if type(result.handled) == "boolean" and result.handled == true then
log.trace(
"Handler ",
node.value.id,
" for "
.. node.value.event
.. " returned handled = true, skipping the rest of the queue."
if type(result) == "table" then
if result.handled == true then
local id = node.value.id or nil ---@diagnostic disable-line
local event = node.value.event or nil ---@diagnostic disable-line
log.trace(
string.format(
"Handler %s for %s returned handled = true, skipping the rest of the queue.",
id,
event
)
return result
end
)
return result
end
end
if not node_is_next then
if result == true then
local next = node.next
self._list:remove_node(node)
node = next
else
node = node.next
end
end
end

function Queue:is_empty()
function M.Queue:is_empty()
return self._list.size == 0
end

function Queue:remove_by_id(id)
function M.Queue:remove_by_id(id)
local current = self._list.head
while current ~= nil do
local is_match = false
local item = current.value
if item ~= nil then
local item_id = item.id or item
if item_id == id then
is_match = true
end
end
if is_match then
local item_id = type(item) == "table" and item.id or item
if item_id == id then
local next = current.next
self._list:remove_node(current)
current = next
Expand All @@ -121,7 +144,8 @@ function Queue:remove_by_id(id)
end
end

return {
Queue = Queue,
LinkedList = LinkedList,
}
function M.Queue:clear()
self._list:clear()
end

return M
11 changes: 9 additions & 2 deletions lua/neo-tree/events/init.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
local vim = vim
local q = require("neo-tree.events.queue")
local log = require("neo-tree.log")
local utils = require("neo-tree.utils")

local M = {
---@enum NeotreeEventEnum
local enum = {
-- Well known event names, you can make up your own
STATE_CREATED = "state_created",
BEFORE_RENDER = "before_render",
Expand Down Expand Up @@ -49,6 +49,13 @@ local M = {
VIM_WIN_ENTER = "vim_win_enter",
}

local M = enum

---@param event_name NeotreeEventEnum
---@param autocmds string[]
---@param debounce_frequency integer|nil
---@param seed_fn (fun(args: table): table)|nil
---@param nested boolean|nil
M.define_autocmd_event = function(event_name, autocmds, debounce_frequency, seed_fn, nested)
local opts = {
setup = function()
Expand Down
72 changes: 51 additions & 21 deletions lua/neo-tree/events/queue.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@ local utils = require("neo-tree.utils")
local log = require("neo-tree.log")
local Queue = require("neo-tree.collections").Queue

---@type table<NeotreeEventEnum, NeotreeQueue<NeotreeConfig.event_handler>>
local event_queues = {}

---@class NeotreeEventOpts
---@field debounce_frequency number|nil
---@field debounce_strategy NeotreeDebounceStrategy|nil
---@field setup function|nil
---@field setup_was_run boolean|nil
---@field teardown function|nil

---@type table<NeotreeEventEnum, NeotreeEventOpts>
local event_definitions = {}

---@class NeotreeEventQueue
local M = {}

---@param event_handler NeotreeConfig.event_handler
local validate_event_handler = function(event_handler)
if type(event_handler) ~= "table" then
error("Event handler must be a table")
Expand All @@ -19,12 +32,15 @@ local validate_event_handler = function(event_handler)
end

M.clear_all_events = function()
for event_name, queue in pairs(event_queues) do
for event_name, _ in pairs(event_queues) do
M.destroy_event(event_name)
end
event_queues = {}
end

---Assign new event. Skips if event already exists.
---@param event_name NeotreeEventEnum
---@param opts NeotreeEventOpts
M.define_event = function(event_name, opts)
local existing = event_definitions[event_name]
if existing ~= nil then
Expand All @@ -33,6 +49,8 @@ M.define_event = function(event_name, opts)
event_definitions[event_name] = opts
end

---Delete event.
---@param event_name NeotreeEventEnum
M.destroy_event = function(event_name)
local existing = event_definitions[event_name]
if existing == nil then
Expand All @@ -45,19 +63,24 @@ M.destroy_event = function(event_name)
end
existing.setup_was_run = false
end
if event_queues[event_name] then
Queue.clear(event_queues[event_name])
end
event_queues[event_name] = nil
return true
end

---Fire event.
---@param event NeotreeEventEnum
---@param args table|nil # Additional args passed to the handler.
---@return table|nil # result of event_handler or nil
local fire_event_internal = function(event, args)
local queue = event_queues[event]
if queue == nil then
return nil
end
--log.trace("Firing event: ", event, " with args: ", args)

if queue:is_empty() then
--log.trace("Event queue is empty")
if Queue.is_empty(queue) then
return nil
end
local seed = utils.get_value(event_definitions, event .. ".seed")
Expand All @@ -72,9 +95,9 @@ local fire_event_internal = function(event, args)
end
end

return queue:for_each(function(event_handler)
local remove_node = event_handler == nil or event_handler.cancelled
if not remove_node then
---@param event_handler NeotreeConfig.event_handler|nil
local run_each_handler = function(event_handler)
if event_handler and not event_handler.cancelled then
local success, result = pcall(event_handler.handler, args)
local id = event_handler.id or event_handler
if success then
Expand All @@ -88,28 +111,38 @@ local fire_event_internal = function(event, args)
end
return result
end
end)
return false
end
return Queue.for_each(queue, run_each_handler)
end

M.fire_event = function(event, args)
local freq = utils.get_value(event_definitions, event .. ".debounce_frequency", 0, true)
local strategy = utils.get_value(event_definitions, event .. ".debounce_strategy", 0, true)
log.trace("Firing event: ", event, " with args: ", args)
---Fire events assigned to event_name.
---@param event_name NeotreeEventEnum
---@param args table|nil # Additional args passed to the handler.
---@return table|nil
M.fire_event = function(event_name, args)
local def = event_definitions[event_name]
local freq = def and def.debounce_frequency or 0
local strategy = def and def.debounce_strategy or 0
log.trace("Firing event: ", event_name, " with args: ", args)
if freq > 0 then
utils.debounce("EVENT_FIRED: " .. event, function()
fire_event_internal(event, args or {})
utils.debounce("EVENT_FIRED: " .. event_name, function()
fire_event_internal(event_name, args or {})
end, freq, strategy)
else
return fire_event_internal(event, args or {})
return fire_event_internal(event_name, args or {})
end
end

---Add a new event_handler
---@param event_handler NeotreeConfig.event_handler
M.subscribe = function(event_handler)
validate_event_handler(event_handler)

local queue = event_queues[event_handler.event]
if queue == nil then
log.debug("Creating queue for event: " .. event_handler.event)
---@type NeotreeQueue<NeotreeConfig.event_handler>
queue = Queue:new()
local def = event_definitions[event_handler.event]
if def and type(def.setup) == "function" then
Expand All @@ -124,20 +157,17 @@ M.subscribe = function(event_handler)
event_queues[event_handler.event] = queue
end
log.debug("Adding event handler [", event_handler.id, "] for event: ", event_handler.event)
queue:add(event_handler)
Queue.add(queue, event_handler)
end

M.unsubscribe = function(event_handler)
local queue = event_queues[event_handler.event]
if queue == nil then
return nil
end
queue:remove_by_id(event_handler.id or event_handler)
if queue:is_empty() then
Queue.remove_by_id(queue, event_handler.id or event_handler)
if Queue.is_empty(queue) then
M.destroy_event(event_handler.event)
event_queues[event_handler.event] = nil
else
event_queues[event_handler.event] = queue
end
end

Expand Down
Loading
Loading