From a3aeefd2124c99f5b65906bb4c30024d9cb9bdfd Mon Sep 17 00:00:00 2001 From: Chris Pecunies Date: Sat, 28 Dec 2024 19:42:25 -0800 Subject: [PATCH] adding event apparatus --- lua/down.lua | 27 +- lua/down/config.lua | 7 +- lua/down/event/init.lua | 191 +++++ lua/down/mod.lua | 406 ++++------ lua/down/mod/cmd/init.lua | 24 +- lua/down/mod/{edit/link => code}/README.md | 0 lua/down/mod/code/init.lua | 23 + .../{edit/syntax => data/bookmark}/README.md | 0 lua/down/mod/data/bookmark/init.lua | 3 + lua/down/mod/data/date/init.lua | 4 +- lua/down/mod/data/init.lua | 53 +- lua/down/mod/data/link/init.lua | 30 +- lua/down/mod/data/store/init.lua | 25 + lua/down/mod/data/tag/init.lua | 34 +- lua/down/mod/data/tag/tag.lua | 35 + lua/down/mod/data/task/agenda/agenda.lua | 8 + lua/down/mod/data/task/task.lua | 16 + lua/down/mod/edit/link/init.lua | 516 ------------- lua/down/mod/edit/syntax/init.lua | 696 ------------------ .../link/parse/init.lua => lsp/README.md} | 0 lua/down/mod/lsp/init.lua | 2 - lua/down/mod/parse/init.lua | 22 + lua/down/mod/ui/calendar/day/init.lua | 21 + lua/down/mod/ui/calendar/time/init.lua | 1 + lua/down/mod/workspace/init.lua | 66 +- lua/down/types/context.lua | 8 +- lua/down/types/data.lua | 20 - lua/down/types/event.lua | 32 + lua/down/types/init.lua | 14 - lua/down/types/mod.lua | 36 +- lua/down/util/event/callback.lua | 41 -- lua/down/util/event/init.lua | 0 lua/down/util/init.lua | 2 +- neovim.yml | 5 + note/2024/12/28.md | 0 35 files changed, 697 insertions(+), 1671 deletions(-) rename lua/down/mod/{edit/link => code}/README.md (100%) create mode 100644 lua/down/mod/code/init.lua rename lua/down/mod/{edit/syntax => data/bookmark}/README.md (100%) create mode 100644 lua/down/mod/data/store/init.lua create mode 100644 lua/down/mod/data/tag/tag.lua create mode 100644 lua/down/mod/data/task/agenda/agenda.lua create mode 100644 lua/down/mod/data/task/task.lua delete mode 100644 lua/down/mod/edit/link/init.lua delete mode 100644 lua/down/mod/edit/syntax/init.lua rename lua/down/mod/{edit/link/parse/init.lua => lsp/README.md} (100%) delete mode 100644 lua/down/types/data.lua delete mode 100644 lua/down/util/event/callback.lua delete mode 100644 lua/down/util/event/init.lua create mode 100644 neovim.yml delete mode 100644 note/2024/12/28.md diff --git a/lua/down.lua b/lua/down.lua index d365977..7ff0680 100644 --- a/lua/down.lua +++ b/lua/down.lua @@ -25,14 +25,14 @@ Down.default = { --- Load the user configuration, and load into config --- defined modules specifieed and workspaces --- @param user down.config.User user config to load ---- @param ... any The arguments to pass into an optional user hook +--- @param ... string The arguments to pass into an optional user hook function Down.setup(user, ...) - Down.config.setup(Down.config, user, Down.default) - Down.start(Down) + Down.config:setup(user, Down.default, ...) + Down:start() end function Down:start() - self.mod.load_mod('workspace', self.config.user.workspace or {}) + Down.mod.load_mod('workspace', self.config.user.workspace or {}) for name, usermod in pairs(self.config.user) do if type(usermod) == 'table' then if name == 'lsp' and self.config.dev == false then @@ -66,23 +66,8 @@ end ---@param e string ---@param ... any function Down:broadcast(e, ...) - self.mod.broadcast({ ---@type down.Event - type = e, ---@diagnostic disable-line - split = { - e, - }, - - file = vim.fn.expand('%:p'), - dir = vim.fn.getcwd(), - topic = e, - ref = 'Down:broadcast', - broadcast = true, - line = vim.api.nvim_get_current_line(), - position = vim.api.nvim_win_get_position(0), - buf = vim.api.nvim_get_current_buf(), - win = vim.api.nvim_get_current_win(), - mode = vim.fn.mode(), - }) + local ev = self.event.define("down", e or "started") ---@type down.Event + self.event.broadcast_to(ev, Down.mod.mods) end --- Test all modules loaded diff --git a/lua/down/config.lua b/lua/down/config.lua index 63e0da1..f392c01 100644 --- a/lua/down/config.lua +++ b/lua/down/config.lua @@ -27,9 +27,10 @@ local Config = { pathsep = osinfo == 'windows' and '\\' or '/', load = { maps = function() - vim.keymap.set('n', ',dw', 'Down', { silent = true }) - vim.keymap.set('n', ',D', 'Down', { silent = true }) - vim.keymap.set('n', '~', 'Down', { silent = true }) + map.n(',dd', 'Down') + map.n(',D', 'Down') + map.n('~', 'Down') + map.n('|', 'Down') end, opts = function() vim.o.conceallevel = 2 diff --git a/lua/down/event/init.lua b/lua/down/event/init.lua index 92c2fee..53f88d2 100644 --- a/lua/down/event/init.lua +++ b/lua/down/event/init.lua @@ -1,4 +1,195 @@ +local log = require 'down.util.log' + ---@type down.Event local Event = { + topic = '', + type = '', + ref = '', + split = {}, + body = nil, + broadcast = true, + position = vim.api.nvim_win_get_cursor(0), + file = vim.fn.expand('%:p'), + dir = vim.fn.getcwd(), + buf = vim.api.nvim_get_current_buf(), + win = vim.api.nvim_get_current_win(), + mode = vim.fn.mode(), + line = vim.api.nvim_win_get_cursor(0)[1], + char = vim.api.nvim_win_get_cursor(0)[2], + ---@type down.Context + context = { + ---@type down.Position + position = { + line = vim.api.nvim_win_get_cursor(0)[1], + char = vim.api.nvim_win_get_cursor(0)[2], + }, + line = vim.api.nvim_get_current_line(), + char = vim.api.nvim_win_get_cursor(0)[2], + file = vim.fn.expand('%:p'), + buf = vim.api.nvim_get_current_buf(), + win = vim.api.nvim_get_current_win(), + dir = vim.fn.getcwd(), + scope = "global" + }, +} +--- @class down.Callback +--- @field callbacks table +local Cb = { + ---@type table + cb = {}, } + + +--- @param ty? string +--- @return { [1]: fun(event: down.Event, content: table|any)> } +function Event:get_cb(ty) + return Cb.cb[ty or self.type] +end + +--- Triggers a new callback to execute whenever an event of the requested type is executed. +--- @param cb fun(event: down.Event, content: table|any) The function to call whenever our event gets triggered. +--- @param filt? fun(event: down.Event): boolean # A filtering function to test if a certain event meets our expectations. +function Event:callback(cb, filt) + Cb.cb[self.type] = Cb.cb[self.type] or {} + table.insert(Cb.cb[self.type], { cb, filt }) +end + +--- @param cb? fun(event: down.Event, content: table|any) +function Event:set_callback(cb) + Cb.cb[self.type] = Cb.cb[self.type] or {} + Cb.cb[self.type]:insert(cb) +end + +--- Used internally by down to call all C with an event. +--- @param self down.Event +function Event.handle(self) + local cbentry = Cb.cb[self.type] + if cbentry then + for _, cb in ipairs(cbentry) do + if not cb[2] or cb[2](self) then + cb[1](self, self.body) + end + end + end +end + +---@type fun(module: down.Mod.Mod, name: string): down.Event +---@return down.Event +Event.define = function(module, name) + local mn = "" + if type(module) == 'table' then + mn = module.name + elseif type(module) == 'string' then + mn = module + end + local type = mn .. '.events.' .. name + return { ---@type down.Event + topic = type, + type = type, + ref = mn, + split = Event.split_type(type) or {}, + body = module, + broadcast = true, + position = vim.api.nvim_win_get_cursor(0), + file = vim.fn.expand('%:p'), + dir = vim.fn.getcwd(), + line = vim.api.nvim_get_current_line(), + buf = vim.api.nvim_get_current_buf(), + win = vim.api.nvim_get_current_win(), + mode = vim.fn.mode(), + context = nil, + } +end + +--- @param type string The full path of a init event +--- @return string[]? +function Event.split_type(type) + local start_str, end_str = type:find('%.events%.') + local split_event_type = { type:sub(0, start_str - 1), type:sub(end_str + 1) } + if #split_event_type ~= 2 then + log.warn('Invalid type name:' .. type) + return + end + return split_event_type +end + +--- Returns an event template defined in `init.events`. +--- @param m down.Mod.Mod A reference to the init invoking the function +--- @param type string A full path to a valid event type (e.g. `init.events.some_event`) +--- @return down.Event? +function Event.get_template(m, type) + local split = Event.split_type(type) + if not split then + log.warn('Unable to get event template for event' .. type .. 'and init' .. m.name) + return + end + + log.trace('Returning' .. split[2] .. 'for init' .. split[1]) + return m.events[split[2]] +end + +--- Returns a copy of the event template provided by a init. +--- @param m down.Mod.Mod A reference to the init invoking the function +--- @param type string A full path to a valid .vent type (e.g. `init.events.some_event`) +--- @param body table|any? The body of the event, can be anything from a string to a table to whatever you please. +--- @param ev? table The original event data. +--- @return down.Event? # New event. +function Event.new(m, type, body, ev) + local split = Event.split_type(type) + local event_template = Event.get_template(m or { name = '' }, type) + if not event_template then + log.warn('Unable to create event of type' .. type .. '. Returning nil...') + return + end + local mn = vim.deepcopy(event_template) + mn.type = type + mn.body = body + mn.ref = m.name + mn.split = assert(split) + mn.file = vim.fn.expand('%:t') --[[@as string]] + mn.dir = vim.fn.expand('%:p:h') --[[@as string]] + mn.buf = ev and ev.buf or vim.api.nvim_get_current_buf() + mn.win = vim.api.nvim_get_current_win() + mn.position = vim.api.nvim_win_get_cursor(mn.win) + mn.mode = vim.api.nvim_get_mode() + mn.line = vim.api.nvim_buf_get_lines(mn.buf, mn.position[1] - 1, mn.position[1], true)[1] + mn.ref = m.name + mn.broadcast = true + return mn +end + +--- Sends an event to all subscribed mod. The event contains the filename, filehead, cursor position and line body as a bonus. +--- @param mods down.Mod.Mod[] +--- @param self down.Event +function Event.broadcast_to(self, mods) + if not self.split then + log.error('Unable to broadcast event of type' .. self.type .. '- invalid event name') + return + end + Event.handle(self) + for _, cm in pairs(mods or {}) do + if cm.subscribed and cm.subscribed[self.split[1]] then + local evt = cm.subscribed[self.split[1]][self.split[2]] + if evt ~= nil and evt == true then + cm.handle(self) + end + end + end +end + +--- @param recv down.Mod.Mod The name of a loaded init that will be the recipient of the event. +--- @return nil +--- @param self down.Event +function Event.send(self, recv) + self.broadcast = false + Event.handle(self) + if recv.subscribed and recv.subscribed[self.split[1]] then + local evt = recv.subscribed[self.split[1]][self.split[2]] + if evt ~= nil and evt == true then + recv.handle(self) + end + end +end + +return Event diff --git a/lua/down/mod.lua b/lua/down/mod.lua index f185a8c..3416634 100644 --- a/lua/down/mod.lua +++ b/lua/down/mod.lua @@ -1,7 +1,7 @@ local uv, lu, fn = vim.loop or vim.uv, vim.lsp.util, vim.fn +local Event = require 'down.event' local util = require 'down.util' local utils = require 'down.util' -local cb = require('down.util.event.callback') local config = require('down.config') local log = require('down.util.log') @@ -11,94 +11,107 @@ local log = require('down.util.log') --- @TODO: Merge Module.data with Module, Module.config as subfield for all modules --- which have class then of down.mod.[Mod] as M ----@class down.mod.base.Base: down.Mod +---@class down.Mod local Mod = { - count = 0, - mods = {}, - ---@class down.mod.base.Config + setup = function() + return { + loaded = true, + replaces = {}, + merge = false, + requires = {}, + } + end, + cmds = function() end, + load = function() + -- print 'default load n' + end, + test = function() end, + post_load = function() + -- print('postload' .. n) + end, + opts = function() end, + maps = function() end, + handle = function(e) + -- print('load' .. e) + end, + name = '', + namespace = '', + data = {}, config = {}, - --- @class down.mod.base.Data - --- @field mods { [string]: down.Mod } - --- @field count integer - default = { - ---@type fun():down.mod.Setup - setup = function() - return { - loaded = true, - replaces = {}, - merge = false, - requires = {}, - wants = {}, - } - end, - }, + events = {}, + subscribed = {}, + required = {}, + import = {}, + tests = {}, } ----@type fun(modn: string, type:string, body: string): down.Event -Mod.default.event = function(modn, name, body) - return { - payload = body, - topic = name, - type = modn .. '.events.' .. name, - ref = modn, - split = {}, - body = body, - broadcast = true, - position = vim.api.nvim_win_get_cursor(0), - file = vim.fn.expand('%:p'), - dir = vim.fn.getcwd(), - line = vim.api.nvim_get_current_line(), - buf = vim.api.nvim_get_current_buf(), - win = vim.api.nvim_get_current_win(), - mode = vim.fn.mode(), - } -end +Mod.mods = {} + +Mod.default = { + mods = { + 'tool.telescope', + 'lsp', + 'note', + 'workspace', + 'data.log', + 'data.template', + }, + ---@type fun():down.mod.Setup + setup = function() + return { + loaded = true, + replaces = {}, + merge = false, + requires = {}, + } + end, + ---@return down.Mod + mod = function(n) + ---@type down.Mod + return setmetatable({ + setup = Mod.setup, + cmds = function() end, + load = function() + -- print 'default load n' + end, + test = function() end, + post_load = function() + -- print('postload' .. n) + end, + opts = function() end, + maps = function() end, + handle = function(e) + -- print('load' .. e) + end, + name = n, + namespace = vim.api.nvim_create_namespace('down.mod.' .. n), + data = {}, + config = {}, + events = {}, + subscribed = {}, + required = {}, + import = {}, + tests = {}, + }, { + __call = function(self, fun, ...) + return self.data[fun](...) + end, + -- __index = function(self, k) + -- return self.required[k] + -- end, + __newindex = function(self, k, v) + self.data[k] = v + end, + __eq = function(m1, m2) + return m1.name == m2.name + end, + -- __tostring = function(m) + -- return m.name + -- end, + }) + end +} ----@return down.Mod -Mod.default.mod = function(n) - ---@type down.Mod - return setmetatable({ - setup = Mod.default.setup, - cmds = function() end, - load = function() - -- print 'default load n' - end, - test = function() end, - post_load = function() - -- print('postload' .. n) - end, - opts = function() end, - maps = function() end, - handle = function(e) - -- print('load' .. e) - end, - name = n, - namespace = vim.api.nvim_create_namespace('down.mod.' .. n), - data = {}, - config = {}, - events = {}, - subscribed = {}, - required = {}, - import = {}, - tests = {}, - }, { - __call = function(self, fun, ...) - return self.data[fun](...) - end, - -- __index = function(self, k) - -- return self.required[k] - -- end, - __newindex = function(self, k, v) - self.data[k] = v - end, - __eq = function(m1, m2) - return m1.name == m2.name - end, - -- __tostring = function(m) - -- return m.name - -- end, - }) -end --- @param nm string --- @param im? string[] @@ -117,26 +130,14 @@ Mod.new = function(nm, im) return n end ----@param m down.Mod -Mod.pre_run = function(m) - m.setup = function() - return { loaded = true } - end - if m.cmds then - m.cmds() - end - if m.maps then - m.maps() - end -end - +--- @param mod string ---@return nil Mod.delete = function(mod) Mod.mods[mod] = nil return nil end ---- @param m down.Mod The actual init to load. +--- @param m down.Mod.Mod The actual init to load. --- @return down.Mod|nil # Whether the init successfully loaded. Mod.load_mod_from_table = function(m, cfg) if Mod.mods[m.name] ~= nil then @@ -149,21 +150,6 @@ Mod.load_mod_from_table = function(m, cfg) mod_to_replace = vim.deepcopy(Mod.mods[mod_load.replaces]) end Mod.mods[m.name] = m - - if mod_load.wants and not vim.tbl_isempty(mod_load.wants) then - for _, req in ipairs(mod_load.wants) do - if not Mod.is_loaded(req) then - if config.user[req] then - if not Mod.load_mod(req) then - return Mod.delete(m.name) - end - else - return Mod.delete(m.name) - end - end - m.required[req] = Mod.mods[req].data - end - end if mod_load.requires and vim.tbl_count(mod_load.requires) then for _, req in pairs(mod_load.requires) do if not Mod.is_loaded(req) then @@ -186,11 +172,25 @@ Mod.load_mod_from_table = function(m, cfg) end m.replaced = true end - Mod.count = Mod.count + 1 Mod.mod_load(m) return Mod.mods[m.name] end +--- @param modn string +--- @return down.config.Mod? +function Mod.check_mod(modn) + local modl = require('down.mod.' .. modn) + if not modl then + log.error('Mod.load_mod: could not load mod ' .. modn) + return nil + end + if modl == true then + log.error('did not return valid mod: ' .. modn) + return nil + end + return modl +end + --- @param modn string A path to a init on disk. A path in down is '.', not '/'. --- @param cfg table? A config that reflects the structure of `down.config.user.setup["init.name"].config`. --- @return down.Mod|nil # Whether the init was successfully loaded. @@ -201,15 +201,8 @@ function Mod.load_mod(modn, cfg) end return Mod.mods[modn] end - local modl = require('down.mod.' .. modn) - if not modl then - log.error('Mod.load_mod: could not load mod ' .. modn) - return nil - end - if modl == true then - log.error('did not return valid mod: ' .. modn) - return nil - end + local modl = Mod.check_mod(modn) + if not modl then return nil end if cfg and not vim.tbl_isempty(cfg) then modl.config = util.extend(modl.config, cfg) end @@ -244,7 +237,7 @@ end --- @return table? function Mod.mod_config(modn) if not Mod.is_loaded(modn) then - log.trace('Attempt to get init config with name', modn, 'failed - init is not loaded.') + log.trace('Attempt to get init config with name' .. modn .. 'failed - init is not loaded.') return end return Mod.mods[modn].config @@ -259,7 +252,6 @@ function Mod.get_mod(modn) log.trace('Attempt to get init with name' .. modn .. 'failed - init is not loaded.') return end - return Mod.mods[modn].data end @@ -289,142 +281,6 @@ function Mod.await(modn, callback) end) end ---- @param type string The full path of a init event ---- @return string[]? -function Mod.split_event_type(type) - local start_str, end_str = type:find('%.events%.') - - local split_event_type = { type:sub(0, start_str - 1), type:sub(end_str + 1) } - - if #split_event_type ~= 2 then - log.warn('Invalid type name:', type) - return - end - - return split_event_type -end - ---- Returns an event template defined in `init.events`. ---- @param m down.Mod A reference to the init invoking the function ---- @param type string A full path to a valid event type (e.g. `init.events.some_event`) ---- @return down.Event? -function Mod.get_event_template(m, type) - if not Mod.is_loaded(m.name) then - log.info('Unable to get event of type' .. type .. 'with init' .. m.name) - return - end - - local split = Mod.split_event_type(type) - - if not split then - log.warn('Unable to get event template for event' .. type .. 'and init' .. m.name) - return - end - - log.trace('Returning' .. split[2] .. 'for init' .. split[1]) - return Mod.mods[m.name].events[split[2]] -end - ---- Creates a deep copy of the `mod.base_event` event and returns it with a custom type and referrer. ---- @param m down.Mod A reference to the init invoking the function. ---- @param name string A relative path to a valid event template. ---- @return down.Event -function Mod.define_event(m, name) - return Mod.default.event(m.name, name, m) -end - ---- Returns a copy of the event template provided by a init. ---- @param m down.Mod A reference to the init invoking the function ---- @param type string A full path to a valid .vent type (e.g. `init.events.some_event`) ---- @param body table|any? The body of the event, can be anything from a string to a table to whatever you please. ---- @param ev? table The original event data. ---- @return down.Event? # New event. -function Mod.new_event(m, type, body, ev) - -- Get the init that contains the event - local modn = Mod.split_event_type(type)[1] - -- Retrieve the template from init.events - local event_template = Mod.get_event_template(Mod.mods[modn] or { name = '' }, type) - - if not event_template then - log.warn('Unable to create event of type' .. type .. '. Returning nil...') - return - end - - -- Make a deep copy here - we don't want to override the actual base table! - local mn = vim.deepcopy(event_template) - - mn.type = type - mn.body = body - mn.ref = m.name - - -- Override all the important values - mn.split = assert(Mod.split_event_type(type)) - mn.file = vim.fn.expand('%:t') --[[@as string]] - mn.dir = vim.fn.expand('%:p:h') --[[@as string]] - local bufid = ev and ev.buf or vim.api.nvim_get_current_buf() - local winid = assert(vim.fn.bufwinid(bufid)) - if winid == -1 then - winid = vim.api.nvim_get_current_win() - end - -- vim.print(mn) - -- - mn.position = vim.api.nvim_win_get_cursor(winid) - local row_1b = mn.position[1] - mn.line = vim.api.nvim_buf_get_lines(bufid, row_1b - 1, row_1b, true)[1] - mn.ref = m.name - mn.broadcast = true - mn.buf = bufid - mn.win = winid - mn.mode = vim.api.nvim_get_mode() - return mn -end - ---- Sends an event to all subscribed mod. The event contains the filename, filehead, cursor position and line body as a bonus. ---- @param event down.Event An event, usually created by `mod.new_event()`. ---- @param callback function? A callback to be invoked after all events have been asynchronously broadcast -function Mod.broadcast(event, callback) - -- Broadcast the event to all mod - -- vim.print(event) - if not event.split then - log.error('Unable to broadcast event of type' .. event.type .. '- invalid event name') - return - end - - cb.handle(event) - - for _, cm in pairs(Mod.mods) do - if cm.subscribed and cm.subscribed[event.split[1]] then - local evt = cm.subscribed[event.split[1]][event.split[2]] - if evt ~= nil and evt == true then - cm.handle(event) - end - end - end - -- TODO: deprecate - if callback then - callback() - end -end - ---- @param recv string The name of a loaded init that will be the recipient of the event. ---- @param ev down.Event An event, usually created by `mod.new_event()`. ---- @return nil -function Mod.send_event(recv, ev) - if not Mod.is_loaded(recv) then - log.warn('Unable to send event to init' .. recv .. '- the init is not loaded.') - return - end - ev.broadcast = false - cb.handle(ev) - local modl = Mod.mods[recv] - if modl.subscribed and modl.subscribed[ev.split[1]] then - local evt = modl.subscribed[ev.split[1]][ev.split[2]] - if evt ~= nil and evt == true then - modl.handle(ev) - end - end -end - ---@param m down.Mod function Mod.mod_load(m) if m.cmds then @@ -441,15 +297,6 @@ function Mod.mod_load(m) end end -Mod.default.mods = { - 'tool.telescope', - 'lsp', - 'note', - -- 'workspace', - 'data.log', - 'data.template', -} - Mod.get = function(m) local path = 'down.mod.' .. m local ok, pc = require(path) @@ -470,6 +317,25 @@ Mod.modules = function(ms) return modmap end +--- @param m down.Mod.Mod +--- @param name string +--- @param body table +--- @param ev? table +--- @return down.Event? +function Mod.new_event(m, type, body, ev) + return Event.new(m, type, body, ev) +end + +---@type fun(module: down.Mod.Mod, name: string): down.Event +---@return down.Event +function Mod.define_event(module, name) + return Event.define(module, name) +end + +function Mod.broadcast(e) + Event.broadcast_to(e, Mod.mods) +end + return setmetatable(Mod, { ---@param self down.mod.base.Base ---@param modname string diff --git a/lua/down/mod/cmd/init.lua b/lua/down/mod/cmd/init.lua index 76962ef..c5defdd 100644 --- a/lua/down/mod/cmd/init.lua +++ b/lua/down/mod/cmd/init.lua @@ -64,7 +64,8 @@ M.data = { return elseif not check_condition(ref.condition) then log.error( - ('Error when executing `:Down %s` - the command is currently disabled. Some commands will only become available under certain conditions, e.g. being within a `.down` file!'):format( + ('Error when executing `:Down %s` - the command is currently disabled. Some commands will only become available under certain conditions, e.g. being within a `.down` file!') + :format( table.concat(vim.list_slice(args, 1, i), ' ') ) ) @@ -103,7 +104,8 @@ M.data = { if not ref.name then log.error( - ("Error when executing `:down %s` - the ending command didn't have a `name` variable associated with it! This is an implementation error on the developer's side, so file a report to the author of the mod."):format( + ("Error when executing `:down %s` - the ending command didn't have a `name` variable associated with it! This is an implementation error on the developer's side, so file a report to the author of the mod.") + :format( data.args ) ) @@ -114,6 +116,7 @@ M.data = { M.events[ref.name] = mod.define_event(M, ref.name) end + mod.broadcast( assert( mod.new_event( @@ -206,11 +209,11 @@ M.data = { -- TODO: Fix `:down m ` giving invalid completions local keys = ref and vim.tbl_keys(ref.subcommands or {}) - or ( - vim.tbl_filter(function(key) - return key:find(splitcmd[#splitcmd]) - end, vim.tbl_keys(last_valid_ref.subcommands or {})) - ) + or ( + vim.tbl_filter(function(key) + return key:find(splitcmd[#splitcmd]) + end, vim.tbl_keys(last_valid_ref.subcommands or {})) + ) table.sort(keys) do @@ -309,8 +312,8 @@ M.data = { if not err then log.warn( 'Could not load command' - .. name - .. 'for init base.cmd - the corresponding mod.lua file does not exist.' + .. name + .. 'for init base.cmd - the corresponding mod.lua file does not exist.' ) return end @@ -375,6 +378,7 @@ M.config = { M.post_load = M.data.sync +---@type down.mod.Handler M.handle = function(event) if event.type == 'cmd.events.mod.setup' then local ok = pcall(mod.load_mod, event.content[1]) @@ -433,7 +437,7 @@ M.handle = function(event) mods_popup:mount() end end ----@class down.mod.cmd.Subscribed +---@class down.mod.cmd.Subscribed: down.mod.Subscribed M.subscribed = { cmd = { -- ["mod.new"] = true, diff --git a/lua/down/mod/edit/link/README.md b/lua/down/mod/code/README.md similarity index 100% rename from lua/down/mod/edit/link/README.md rename to lua/down/mod/code/README.md diff --git a/lua/down/mod/code/init.lua b/lua/down/mod/code/init.lua new file mode 100644 index 0000000..41c3b6f --- /dev/null +++ b/lua/down/mod/code/init.lua @@ -0,0 +1,23 @@ +local mod = require('down.mod') + +--- @class down.mod.Code: down.Mod +local Code = mod.new('code') + +--- @class down.mod.code.Config +--- @field languages string[] +Code.config = { + --- What languages to support + languages = {}, +} + +--- @class down.mod.code.Data +Code.data = {} + +Code.setup = function() + return { + loaded = true, + requires = {}, + } +end + +return Code diff --git a/lua/down/mod/edit/syntax/README.md b/lua/down/mod/data/bookmark/README.md similarity index 100% rename from lua/down/mod/edit/syntax/README.md rename to lua/down/mod/data/bookmark/README.md diff --git a/lua/down/mod/data/bookmark/init.lua b/lua/down/mod/data/bookmark/init.lua index a146280..7b79b4c 100644 --- a/lua/down/mod/data/bookmark/init.lua +++ b/lua/down/mod/data/bookmark/init.lua @@ -17,6 +17,7 @@ B.data = { } } +---@return down.mod.Setup B.setup = function() return { loaded = true, @@ -59,3 +60,5 @@ B.handle = function(e) print('es2 list') end end + +return B diff --git a/lua/down/mod/data/date/init.lua b/lua/down/mod/data/date/init.lua index d3a5d01..c574a1d 100644 --- a/lua/down/mod/data/date/init.lua +++ b/lua/down/mod/data/date/init.lua @@ -1,9 +1,9 @@ ----@type down.Mod +---@class down.mod.data.Date: down.Mod local M = require "down.mod".new("data.date", { }) --- The date data structure. ---- @class down.data.date.Data Data +--- @class down.mod.data.date.Data Data M.data = { } diff --git a/lua/down/mod/data/init.lua b/lua/down/mod/data/init.lua index db33329..a71d9f9 100644 --- a/lua/down/mod/data/init.lua +++ b/lua/down/mod/data/init.lua @@ -1,37 +1,21 @@ local mod = require('down.mod') local config = require("down.config") ----@type down.Mod -local M = mod.new('data', { - -- "log", - -- "store", - -- "task", - -- "mod", - -- "sync", - -- "dirs", - -- "tag", - -- "clipboard", - -- "media", - -- "template", - -- "metadata", - -- "todo", - -- "task", - -- "save" - -- "code", -}) +---@class down.mod.Data: down.Mod +local M = mod.new('data', {}) ---@type down.Store M.data.files = { store = {}, } +--- @return down.mod.Setup M.setup = function() vim.api.nvim_create_autocmd('VimLeavePre', { callback = function() M.data.flush() end, }) - M.data.sync() ---@type down.mod.Setup return { @@ -40,18 +24,49 @@ M.setup = function() } end +M.load = function() + +end + ---@class down.mod.data.Config M.config = { path = vim.fn.stdpath('data') .. '/down.mpack', + dir = { + vim = vim.fs.joinpath(vim.fn.stdpath('data'), 'down/'), + home = vim.fs.joinpath(os.getenv("HOME") or "~/", ".down/"), + }, + file = { + vim = vim.fs.joinpath(vim.fn.stdpath('data'), 'down/', 'down.json'), + home = vim.fs.joinpath(os.getenv("HOME") or "~/", ".down/", 'down.json'), + } } ---@class down.mod.data.Data M.data = { data = {} } + M.data.concat = function(p1, p2) return table.concat({ p1, config.pathsep, p2 }) end +--- @param path string +--- @param cond? fun(name: string, ends: string): boolean +--- @return table +M.data.files = function(path, cond) + local f = {} + local dir = path or vim.fs.root(vim.fn.cwd(), ".down/") + for name, type in vim.fs.dir(dir) do + if type == 'file' and cond or name:endswith(".md") then + table.insert(f, name) + elseif type == 'directory' and not name:startswith(".") then + local fs = M.data.get_files(M.data.concat(path, name)) + for _, v in ipairs(fs) do + table.insert(f, v) + end + end + end +end + M.data.directory_map = function(path, callback) for name, type in vim.fs.dir(path) do if type == 'directory' then diff --git a/lua/down/mod/data/link/init.lua b/lua/down/mod/data/link/init.lua index fcd0a60..d766c3a 100644 --- a/lua/down/mod/data/link/init.lua +++ b/lua/down/mod/data/link/init.lua @@ -14,7 +14,7 @@ Link.setup = function() loaded = true, requires = { 'tool.treesitter', --- For treesitter node parsing - 'workspace', --- For checking filetype and index file names of current workspace + 'workspace', --- For checking filetype and index file names of current workspace }, } end @@ -73,11 +73,12 @@ end ---@param ln string ---@return string, "local" | "web" | "heading" Link.data.resolve = function(ln) - if ln:sub(1, 1) == config.pathsep then + local ch = ln:sub(1, 1) + if ch == config.pathsep then return ln, 'local' - elseif ln:sub(1, 1) == '#' then + elseif ch == '#' then return ln:sub(2), 'heading' - elseif ln:sub(1, 1) == '~' then + elseif ch == '~' then return os.getenv('HOME') .. config.pathsep .. ln:sub(2), 'local' elseif ln:sub(1, 8) == 'https://' or ln:sub(1, 7) == 'http://' then return ln, 'web' @@ -112,7 +113,7 @@ end Link.data.ref = function(node) local link_label = Link.data.text(node) for _, captures, _ in - Link.required['tool.treesitter'].query([[ + Link.required['tool.treesitter'].query([[ (link_reference_definition (link_label) @label (#eq? @label "]] .. link_label .. [[") (link_destination) @link_destination @@ -135,19 +136,24 @@ end --- If either are, then returns the link destination, otherwise nil --- @return string|nil Link.data.iswikilink = function(node, parent) - if not node then + if not node and not parent then return nil - elseif not parent then + elseif node and not parent then + -- print('node and not parent: ', node:type(), Link.data.text(node)) local wikilink = vim.treesitter.get_node_text(node, 0):iswikilink() if wikilink then return wikilink + else end + else + -- print(node:type(), Link.data.text(node)) + -- print(parent:type(), Link.data.text(parent)) + local wikilink = vim.treesitter.get_node_text(parent, 0):iswikilink() + if wikilink then + return wikilink + end + return nil end - local wikilink = vim.treesitter.get_node_text(parent, 0):iswikilink() - if wikilink then - return wikilink - end - return nil end Link.data.destination = function() diff --git a/lua/down/mod/data/store/init.lua b/lua/down/mod/data/store/init.lua new file mode 100644 index 0000000..c781b34 --- /dev/null +++ b/lua/down/mod/data/store/init.lua @@ -0,0 +1,25 @@ +local mod = require 'down.mod' + +---@class down.mod.data.Store: down.Mod +local Store = mod.new('data.store') + +Store.setup = function() + return { + loaded = true, + requires = { + 'data', + } + } +end + +--- @class down.mod.data.store.Config +Store.config = { + +} + +--- @class down.mod.data.store.Data +Store.data = { + +} + +return Store diff --git a/lua/down/mod/data/tag/init.lua b/lua/down/mod/data/tag/init.lua index 8e0d1b5..a3ab930 100644 --- a/lua/down/mod/data/tag/init.lua +++ b/lua/down/mod/data/tag/init.lua @@ -1,4 +1,5 @@ local mod = require("down.mod") +local Tag = require("down.mod.data.tag.tag") local M = require("down.mod").new("data.tag") ---@return down.mod.Setup @@ -31,7 +32,38 @@ M.setup = function() end ---@class down.mod.data.tag.Data -M.data = {} +M.data = { + tags = { + global = { + + }, + workspace = { + + }, + document = { + + } + } +} + +--- Parse a single line for tag instances +--- @param ln string +--- @return string[] +M.data.parse_ln = function(ln) + local tags = {} + for tag in ln:gmatch("#%S+") do + table.insert(tags, tag) + end + print(tags) + return tags +end + +M.data.parse = function(text) + tags = {} + for ln in text:gmatch("[^\n]+") do + M.data.parse_ln(ln) + end +end ---@class down.mod.data.tag.Config M.config = {} diff --git a/lua/down/mod/data/tag/tag.lua b/lua/down/mod/data/tag/tag.lua new file mode 100644 index 0000000..647d6d0 --- /dev/null +++ b/lua/down/mod/data/tag/tag.lua @@ -0,0 +1,35 @@ +--- @class (exact) down.Tag +--- @field tag string +--- @field ln number +--- @field col number +--- @field uri string +--- @field workspace string + +---@type down.Tag +local Tag = setmetatable({ + tag = '', + uri = '', + time = 0, + pos = { + ln = 0, + col = 0, + }, +}, { +}) + +function Tag.new(tag, uri, ln, col) + return { + tag = tag, + uri = uri or vim.fn.expand("%:p"), + time = os.time(), + pos = { + ln = ln or vim.fn.nvim_win_get_cursor(0)[1], + col = col or vim.fn.nvim_win_get_cursor(0)[2], + } + } +end + +function Tag:save() +end + +return Tag diff --git a/lua/down/mod/data/task/agenda/agenda.lua b/lua/down/mod/data/task/agenda/agenda.lua new file mode 100644 index 0000000..7d067d5 --- /dev/null +++ b/lua/down/mod/data/task/agenda/agenda.lua @@ -0,0 +1,8 @@ +local Agenda = { + tasks = { + + }, + uri = "", +} + +return Agenda diff --git a/lua/down/mod/data/task/task.lua b/lua/down/mod/data/task/task.lua new file mode 100644 index 0000000..6021879 --- /dev/null +++ b/lua/down/mod/data/task/task.lua @@ -0,0 +1,16 @@ + +---@enum down.task.Status +local Status = { + "done", + "in_progress", + "cancelled", + "postponed", +} + + +local Task = { + ---@type down.Task.Status + status = "", +} + +return Task diff --git a/lua/down/mod/edit/link/init.lua b/lua/down/mod/edit/link/init.lua deleted file mode 100644 index da51bb1..0000000 --- a/lua/down/mod/edit/link/init.lua +++ /dev/null @@ -1,516 +0,0 @@ -local down = require("down") -local lib, mod, util = down.lib, down.mod - -local M = mod.new("edit.link") -M.setup = function() - return { - loaded = true, - requires = { - "workspace", - "data", - }, - } -end - - ----@class down.edit.link.Config -M.config = { - conceal = true, - style = "markdown", - implicit_extension = nil, - transform_implicit = false, - create_on_follow_fail = true, - context = 0, - name_as_source = false, - transform_explicit = function(text) - text = text:gsub("[ /]", "-") - text = text:lower() - text = os.date("%Y-%m-%d_") .. text - return text - end, -} ----@class down.edit.link.Data -M.data = { - contains = function(start_row, start_col, end_row, end_col, cur_row, cur_col) - local contained = cur_row > start_row and cur_row < end_row - if cur_row == start_row and start_row == end_row then - contained = cur_col > start_col - 1 and cur_col <= end_col - elseif cur_row == start_row then - contained = cur_col > start_col - 1 - elseif cur_row == end_row then - contained = cur_col <= end_col - end - return contained - end, - getLinkUnderCursor = function(col) - local position = vim.api.nvim_win_get_cursor(0) - local capture, start_row, start_col, end_row, end_col, match, match_lines - col = col or position[2] - local patterns = { - md_link = "(%b[]%b())", - wiki_link = "(%[%b[]%])", - ref_style_link = "(%b[]%s?%b[])", - auto_link = "(%b<>)", - citation = "[^%a%d]-(@[%a%d_%.%-']*[%a%d]+)[%s%p%c]?", - } - local row = position[1] - local lines = vim.api.nvim_buf_get_lines( - 0, - row - 1 - M.config.context, - row + M.config.context, - false - ) - -- Iterate through the patterns to see if there's a matching link under the cursor - for link_type, pattern in pairs(patterns) do - local init_row, init_col = 1, 1 - local continue = true - while continue do - -- Look for the pattern in the line(s) - --link_start, link_finish, capture = string.find(lines, pattern, init) - start_row, start_col, end_row, end_col, capture, match_lines = - utils.mFind( - lines, - pattern, - row - M.config.context, - init_row, - init_col - ) - if start_row and link_type == "citation" then - local possessor = string.gsub(capture, "'s$", "") -- Remove Saxon genitive if it's on the end of the citekey - if #capture > #possessor then - capture = possessor - end_col = end_col - 2 - end - end - -- Check for overlap w/ cursor - if start_row then -- There's a match - local overlaps = M.data.contains( - start_row, - start_col, - end_row, - end_col, - position[1], - position[2] + 1 - ) - if overlaps then - match = capture - continue = false - else - init_row, init_col = end_row, end_col - end - else - continue = false - end - end - if match then -- Return the match and type of link if there was a match - return { - match, - match_lines, - link_type, - start_row, - start_col, - end_row, - end_col, - } - end - end - end, - formatLink = function(text, source, part) - local replacement, path_text - -- If the text starts with a hash, format the link as an anchor link - if string.sub(text, 0, 1) == "#" and not source then - path_text = string.gsub(text, "[^%a%s%d%-_]", "") - text = string.gsub(text, "^#* *", "") - path_text = string.gsub(path_text, "^ ", "") - path_text = string.gsub(path_text, " ", "-") - path_text = string.gsub(path_text, "%-%-", "-") - path_text = "#" .. string.lower(path_text) - elseif not source then - path_text = M.transformPath(text) - -- If no path_text, end here - if not path_text then - return - end - if not M.config.implicit_extension then - path_text = path_text .. ".md" - end - else - path_text = source - end - -- Format the replacement depending on the user's link style preference - if M.config.style == "wiki" then - replacement = ( - M.config.name_as_source and { "[[" .. text .. "]]" } - ) or { "[[" .. path_text .. "|" .. text .. "]]" } - else - replacement = { "[" .. text .. "]" .. "(" .. path_text .. ")" } - end - -- Return the requested part - if part == nil then - return replacement - elseif part == 1 then - return text - elseif part == 2 then - return path_text - end - end, - -- TS query strings for different link targets - ---@param link_type "generic" | "definition" | "footnote" | string - get_link_target_query_string = function(link_type) - return lib.match(link_type)({ - generic = [[ - [(_ - [(strong_carryover_set - (strong_carryover - name: (tag_name) @tag_name - (tag_parameters) @title - (#eq? @tag_name "name"))) - (weak_carryover_set - (weak_carryover - name: (tag_name) @tag_name - (tag_parameters) @title - (#eq? @tag_name "name")))]? - title: (paragraph_segment) @title) - (inline_link_target - (paragraph) @title)] - ]], - - [{ "definition", "footnote" }] = string.format( - [[ - (%s_list - (strong_carryover_set - (strong_carryover - name: (tag_name) @tag_name - (tag_parameters) @title - (#eq? @tag_name "name")))? - . - [(single_%s - (weak_carryover_set - (weak_carryover - name: (tag_name) @tag_name - (tag_parameters) @title - (#eq? @tag_name "name")))? - (single_%s_prefix) - title: (paragraph_segment) @title) - (multi_%s - (weak_carryover_set - (weak_carryover - name: (tag_name) @tag_name - (tag_parameters) @title - (#eq? @tag_name "name")))? - (multi_%s_prefix) - title: (paragraph_segment) @title)]) - ]], - lib.reparg(link_type, 5) - ), - _ = string.format( - [[ - (%s - [(strong_carryover_set - (strong_carryover - name: (tag_name) @tag_name - (tag_parameters) @title - (#eq? @tag_name "name"))) - (weak_carryover_set - (weak_carryover - name: (tag_name) @tag_name - (tag_parameters) @title - (#eq? @tag_name "name")))]? - (%s_prefix) - title: (paragraph_segment) @title) - ]], - lib.reparg(link_type, 2) - ), - }) - end, - createLink = function(args) - args = args or {} - local from_clipboard = args.from_clipboard or false - local range = args.range or false - -- Get mode from vim - local mode = vim.api.nvim_get_mode()["mode"] - -- Get the cursor position - local position = vim.api.nvim_win_get_cursor(0) - local row = position[1] - local col = position[2] - -- If the current mode is 'normal', make link from down under cursor - if mode == "n" and not range then - -- Get the text of the line the cursor is on - local line = vim.api.nvim_get_current_line() - local url_start, url_end = M.data.hasUrl(line, "positions", col) - if url_start and url_end then - -- Prepare the replacement - local url = line:sub(url_start, url_end - 1) - local replacement = ( - M.config.links.style == "wiki" and { "[[" .. url .. "|]]" } - ) or { "[]" .. "(" .. url .. ")" } - -- Replace - vim.api.nvim_buf_set_text( - 0, - row - 1, - url_start - 1, - row - 1, - url_end - 1, - replacement - ) - -- Move the cursor to the name part of the link and change mode - if M.config.links.style == "wiki" then - vim.api.nvim_win_set_cursor(0, { row, url_end + 2 }) - else - vim.api.nvim_win_set_cursor(0, { row, url_start }) - end - vim.cmd("startinsert") - else - -- Get the down under the cursor - local cursor_down = vim.fn.expand("") - -- Make a markdown link out of the date and cursor - local replacement - if from_clipboard then - replacement = M.data.formatLink(cursor_down, vim.fn.getreg("+")) - else - replacement = M.data.formatLink(cursor_down) - end - -- If there's no replacement, stop here - if not replacement then - return - end - -- Find the (first) position of the matched down in the line - local left, right = string.find(line, cursor_down, nil, true) - -- Make sure it's not a duplicate of the down under the cursor, and if it - -- is, perform the search until a match is found whose right edge follows - -- the cursor position - if cursor_down ~= "" then - for _left, _right in utils.gmatch(line, cursor_down) do - if _right >= col then - left = _left - right = _right - break - end - end - else - left, right = col + 1, col - end - -- Replace the down under the cursor w/ the formatted link replacement - vim.api.nvim_buf_set_text( - 0, - row - 1, - left - 1, - row - 1, - right, - replacement - ) - vim.api.nvim_win_set_cursor(0, { row, col + 1 }) - end - -- If current mode is 'visual', make link from selection - elseif mode == "v" or range then - -- Get the start of the visual selection (the end is the cursor position) - local vis = vim.fn.getpos("v") - -- If the start of the visual selection is after the cursor position, - -- use the cursor position as start and the visual position as finish - local inverted = range and false or vis[3] > col - local start, finish - if range then - start = vim.api.nvim_buf_get_mark(0, "<") - finish = vim.api.nvim_buf_get_mark(0, ">") - -- Update char offsets - start[1] = start[1] - 1 - finish[1] = finish[1] - 1 - else - start = (inverted and { row - 1, col }) - or { vis[2] - 1, vis[3] - 1 + vis[4] } - finish = (inverted and { vis[2] - 1, vis[3] - 1 + vis[4] }) - or { row - 1, col } - end - local start_row = (inverted and row - 1) or vis[2] - 1 - local start_col = (inverted and col) or vis[3] - 1 - local end_row = (inverted and vis[2] - 1) or row - 1 - -- If inverted, use the col value from the visual selection; otherwise, use the col value - -- from start. - local end_col = (inverted and vis[3]) or finish[2] + 1 - -- Make sure the selection is on a single line; otherwise, do nothing & throw a warning - if start_row == end_row then - local lines = - vim.api.nvim_buf_get_lines(0, start[1], finish[1] + 1, false) - - -- Check if last byte is part of a multibyte character & adjust end index if so - local is_multibyte_char = utils.isMultibyteChar({ - buffer = 0, - row = finish[1], - start_col = end_col, - }) - if is_multibyte_char then - end_col = is_multibyte_char["finish"] - end - - -- Reduce the text only to the visual selection - lines[1] = lines[1]:sub(start_col + 1, end_col) - - -- If start and end are on different rows, reduce the text on the last line to the visual - -- selection as well - if start[1] ~= finish[1] then - lines[#lines] = lines[#lines]:sub(start_col + 1, end_col) - end - -- Save the text selection & format as a link - local text = table.concat(lines) - local replacement = from_clipboard - and M.data.formatLink(text, vim.fn.getreg("+")) - or M.data.formatLink(text) - -- If no replacement, end here - if not replacement then - return - end - -- Replace the visual selection w/ the formatted link replacement - vim.api.nvim_buf_set_text( - 0, - start_row, - start_col, - end_row, - end_col, - replacement - ) - -- Leave visual mode - vim.api.nvim_feedkeys( - vim.api.nvim_replace_termcodes("", true, false, true), - "x", - true - ) - -- Retain original cursor position - vim.api.nvim_win_set_cursor(0, { row, col + 1 }) - else - vim.api.nvim_echo({ - { - "⬇️ Creating links from multi-line visual selection not supported", - "WarningMsg", - }, - }, true, {}) - end - end - end, - - destroyLink = function() - -- Get link name, indices, and row the cursor is currently on - local link = M.data.getLinkUnderCursor() - if link then - local link_name = M.data.getLinkPart(link, "name") - -- Replace the link with just the name - vim.api.nvim_buf_set_text( - 0, - link[4] - 1, - link[5] - 1, - link[6] - 1, - link[7], - { link_name } - ) - else - vim.api.nvim_echo({ - { - "⬇️ Couldn't find a link under the cursor to destroy!", - "WarningMsg", - }, - }, true, {}) - end - end, - - --[[ -followLink() passes a path and anchor (passed in or picked up from a link under -the cursor) to handlePath from the paths module. If no path or anchor are passed -in and there is no link under the cursor, createLink() is called to create a -link from the down under the cursor or a visual selection (if there is one). ---]] - followLink = function(args) - args = args or {} - local path = args.path - local anchor = args.anchor - local range = args.range or false - local link_type - if path or anchor then - path, anchor = path, anchor - else - path, anchor, link_type = - M.data.getLinkPart(M.data.getLinkUnderCursor(), "source") - end - if path then - require("mkdnflow").paths.handlePath(path, anchor) - elseif link_type == "ref_style_link" then -- If this condition is met, no reference was found - vim.api.nvim_echo({ - { "⬇️ Couldn't find a matching reference label!", "WarningMsg" }, - }, true, {}) - elseif M.config.links.new_on_follow_fail then - M.data.newLink({ range = range }) - end - end, - transformPath = function(text) - if - type(M.config.links.transform_explicit) ~= "function" - or not M.config.links.transform_explicit - then - return text - else - return (M.config.links.transform_explicit(text)) - end - end, - get_ref = function(refnr, start_row) - start_row = start_row or vim.api.nvim_win_get_cursor(0)[1] - local row = start_row + 1 - local line_count, continue = vim.api.nvim_buf_line_count(0), true - -- Look for reference - while continue and row <= line_count do - local line = vim.api.nvim_buf_get_lines(0, row - 1, row, false)[1] - local start, finish, match = - string.find(line, "^(%[" .. refnr .. "%]: .*)") - if match then - local _, label_finish = string.find(match, "^%[.-%]: ") - continue = false - return string.sub(match, label_finish + 1), - row, - label_finish + 1, - finish - else - row = row + 1 - end - end - end, -} - -M.load = function() - -- mod.await("cmd", function(cmd) - -- cmd.add_commands_from_table({ - -- link = { - -- name = "link", - -- subcommands = { - -- update = { - -- args = 0, - -- name = "link.new", - -- }, - -- insert = { - -- name = "link.backlinks", - -- args = 0, - -- }, - -- }, - -- }, - -- }) - -- end) -end -M.data.pathType = function(path, anchor) - if not path then - return nil - elseif string.find(path, "^file:") then - return "file" - elseif string.find(path, "https://") then - return "url" - elseif string.find(path, "^@") then - return "citation" - elseif path == "" and anchor then - return "anchor" - else - return "nb_page" - end -end -M.subscribed = { - cmd = { - ["link.new"] = true, - ["link.backlinks"] = true, - }, -} - -return M diff --git a/lua/down/mod/edit/syntax/init.lua b/lua/down/mod/edit/syntax/init.lua deleted file mode 100644 index 9d8c7cc..0000000 --- a/lua/down/mod/edit/syntax/init.lua +++ /dev/null @@ -1,696 +0,0 @@ -local down = require("down") -local mod, utils = down.mod, down.utils - -local M = mod.create("edit.syntax") - -local function schedule(func) - vim.schedule(function() - if - M.data.disable_deferred_updates - or ( - ( - M.data.debounce_counters[vim.api.nvim_win_get_cursor(0)[1] + 1] - or 0 - ) >= M.config.performance.max_debounce - ) - then - return - end - - func() - end) -end - -M.setup = function() - return { - loaded = true, - requires = { - "tool.treesitter", - }, - } -end - ----@class down.edit.syntax.Data -M.data = { - largest_change_start = -1, - largest_change_end = -1, - - last_change = { - active = false, - line = 0, - }, - - -- we need to track the buffers in use - last_buffer = "", - - disable_deferred_updates = false, - debounce_counters = {}, - - code_block_table = { - --[[ - table is setup like so - { - buf_name_1 = {loaded_regex = {regex_name = {type = "type", range = {start_row1 = end_row1}}}} - buf_name_2 = {loaded_regex = {regex_name = {type = "type", range = {start_row1 = end_row1}}}} - } - --]] - }, - - available_languages = {}, - - -- fills M.data.ooaded_code_blocks with the list of active code blocks in the buffer - -- stores globally apparently - check_code_block_type = function(buf, reload, from, to) - -- parse the current buffer, and clear out the buffer's loaded code blocks if needed - local current_buf = vim.api.nvim_buf_get_name(buf) - - -- load nil table with empty values - if M.data.code_block_table[current_buf] == nil then - M.data.code_block_table[current_buf] = { loaded_regex = {} } - end - - -- recreate table for buffer on buffer change - -- reason for existence: - --[[ - user deletes a bunch of code blocks from file, and said code blocks - were the only regex blocks of that language. on a full buffer refresh - like reentering the buffer, this will get cleared to recreate what languages - are loaded. then another function will handle unloading syntax files on next load - --]] - for key in pairs(M.data.code_block_table) do - if current_buf == key and reload == true then - for k, _ in - pairs(M.data.code_block_table[current_buf].loaded_regex) - do - M.data.remove_syntax( - string.format("textGroup%s", string.upper(k)), - string.format("textSnip%s", string.upper(k)) - ) - M.data.code_block_table[current_buf].loaded_regex[k] = nil - end - end - end - - -- If the tree is valid then attempt to perform the query - local tree = M.required["tool.treesitter"].get_document_root(buf) - - if tree then - -- get the language node used by the code block - local code_lang = utils.ts_parse_query( - "markdown", - [[( - (ranged_verbatim_tag (tag_name) @_tagname (tag_parameters) @language) - (#any-of? @_tagname "code" "embed") - )]] - ) - - -- check for each code block capture in the root with a language paramater - -- to build a table of all the languages for a given buffer - local compare_table = {} -- a table to compare to what was loaded - for id, node in - code_lang:iter_captures(tree:root(), buf, from or 0, to or -1) - do - if id == 2 then -- id 2 here refers to the "language" tag - -- find the end node of a block so we can grab the row - local end_node = node:next_named_sibling():next_sibling() - -- get the start and ends of the current capture - local start_row = node:range() + 1 - local end_row - - -- don't try to parse a nil value - if end_node == nil then - end_row = start_row + 1 - else - end_row = end_node:range() + 1 - end - - local regex_lang = vim.treesitter.get_node_text(node, buf) - - -- make sure that the language is actually valid - local type_func = function() - return M.data.available_languages[regex_lang].type - end - local ok, type = pcall(type_func) - - if not ok then - type = "null" -- null type will never get parsed like treesitter languages - end - - -- add language to table - -- if type is empty it means this language has never been found - if - M.data.code_block_table[current_buf].loaded_regex[regex_lang] - == nil - then - M.data.code_block_table[current_buf].loaded_regex[regex_lang] = - { - type = type, - range = {}, - cluster = "", - } - end - -- else just do what we need to do - M.data.code_block_table[current_buf].loaded_regex[regex_lang].range[start_row] = - end_row - table.insert(compare_table, regex_lang) - end - end - - -- compare loaded languages to see if the file actually has the code blocks - if from == nil then - for lang in - pairs(M.data.code_block_table[current_buf].loaded_regex) - do - local found_lang = false - for _, matched in pairs(compare_table) do - if matched == lang then - found_lang = true - break - end - end - -- if no lang was matched, means we didn't find a language in our parse - -- remove the syntax include and region - if found_lang == false then - -- delete loaded lang from the table - M.data.code_block_table[current_buf].loaded_regex[lang] = nil - M.data.remove_syntax( - string.format("textGroup%s", string.upper(lang)), - string.format("textSnip%s", string.upper(lang)) - ) - end - end - end - end - end, - - -- load syntax files for regex code blocks - trigger_highlight_regex_code_block = function( - buf, - remove, - ignore_buf, - from, - to - ) - -- scheduling this function seems to break parsing properly - -- schedule(function() - local current_buf = vim.api.nvim_buf_get_name(buf) - -- only parse from the loaded_code_blocks M, not from the file directly - if M.data.code_block_table[current_buf] == nil then - return - end - local lang_table = M.data.code_block_table[current_buf].loaded_regex - for lang_name, curr_table in pairs(lang_table) do - if curr_table.type == "syntax" then - -- NOTE: the regex fallback code was originally mostly adapted from Vimwiki - -- In its current form it has been intensely expanded upon - local group = string.format("textGroup%s", string.upper(lang_name)) - local snip = string.format("textSnip%s", string.upper(lang_name)) - local start_marker = string.format("@code %s", lang_name) - local end_marker = "@end" - local has_syntax = string.format("syntax list @%s", group) - - -- sync groups when needed - if - ignore_buf == false - and vim.api.nvim_buf_get_name(buf) == M.data.last_buffer - then - M.data.sync_regex_code_blocks(buf, lang_name, from, to) - end - - -- try removing syntax before doing anything - -- fixes hi link groups from not loading on certain updates - if remove == true then - M.data.remove_syntax(group, snip) - end - - --- @type boolean, string|{ output: string } - local ok, result = - pcall(vim.api.nvim_exec2, has_syntax, { output = true }) - - result = result.output or result - - local count = select(2, result:gsub("\n", "\n")) -- get length of result from syn list - local empty_result = 0 - -- look to see if the textGroup is actually empty - -- clusters don't delete when they're clear - for line in result:gmatch("([^\n]*)\n?") do - empty_result = string.match(line, "textGroup%w+%s+cluster=NONE") - if empty_result == nil then - empty_result = 0 - else - empty_result = #empty_result - break - end - end - - -- see if the syntax files even exist before we try to call them - -- if syn list was an error, or if it was an empty result - if - ok == false - or ( - ok == true - and ( - (string.sub(result, 1, 1) == ("N" or "V") and count == 0) - or (empty_result > 0) - ) - ) - then - -- absorb all syntax stuff - -- potentially needs to be expanded upon as bad values come in - local is_keydown = vim.bo[buf].iskeydown - local current_syntax = "" - local foldmethod = vim.o.foldmethod - local foldexpr = vim.o.foldexpr - local foldtext = vim.o.foldtext - local foldnestmax = vim.o.foldnestmax - local foldcolumn = vim.o.foldcolumn - local foldenable = vim.o.foldenable - local foldminlines = vim.o.foldminlines - if vim.b.current_syntax ~= "" or vim.b.current_syntax ~= nil then - current_syntax = lang_name - vim.b.current_syntax = nil ---@diagnostic disable-line - end - - -- include the cluster that will put inside the region - -- source using the available languages - for syntax, table in pairs(M.data.available_languages) do - if table.type == "syntax" then - if lang_name == syntax then - if empty_result == 0 then - -- get the file name for the syntax file - --- @type string|string[] - local file = vim.api.nvim_get_runtime_file( - string.format("syntax/%s.vim", syntax), - false - ) - if file == nil then - file = vim.api.nvim_get_runtime_file( - string.format("after/syntax/%s.vim", syntax), - false - ) - end - - file = file[1] - - local command = - string.format("syntax include @%s %s", group, file) - vim.cmd(command) - - -- make sure that group has things when needed - local regex = group .. "%s+cluster=(.+)" - --- @type boolean, string|{ output: string } - local _, found_cluster = pcall( - vim.api.nvim_exec2, - string.format("syntax list @%s", group), - { output = true } - ) - - found_cluster = found_cluster.output or found_cluster - - local actual_cluster - for match in found_cluster:gmatch(regex) do - actual_cluster = match - end - if actual_cluster ~= nil then - M.data.code_block_table[current_buf].loaded_regex[lang_name].cluster = - actual_cluster - end - elseif - M.data.code_block_table[current_buf].loaded_regex[lang_name].cluster - ~= nil - then - local command = string.format( - "silent! syntax cluster %s add=%s", - group, - M.data.code_block_table[current_buf].loaded_regex[lang_name].cluster - ) - vim.cmd(command) - end - end - end - end - - -- reset some values after including - vim.bo[buf].iskeydown = is_keydown - vim.b.current_syntax = current_syntax or "" ---@diagnostic disable-line - - has_syntax = string.format("syntax list %s", snip) - --- @type boolean, string|{ output: string } - _, result = pcall(vim.api.nvim_exec2, has_syntax, { output = true }) - result = result.output or result - count = select(2, result:gsub("\n", "\n")) -- get length of result from syn list - - --[[ - if we see "-" it means there potentially is already a region for this lang - we must have only 1 line, more lines means there is a region already - see :h syn-list for the format - --]] - if count == 0 or (string.sub(result, 1, 1) == "-" and count == 0) then - -- set highlight groups - local regex_fallback_hl = string.format( - [[ - syntax region %s - \ matchgroup=Snip - \ start="%s" end="%s" - \ contains=@%s - \ keepend - ]], - snip, - start_marker, - end_marker, - group - ) - vim.cmd(string.format("%s", regex_fallback_hl)) - -- sync everything - M.data.sync_regex_code_blocks(buf, lang_name, from, to) - end - - vim.o.foldmethod = foldmethod - vim.o.foldexpr = foldexpr - vim.o.foldtext = foldtext - vim.o.foldnestmax = foldnestmax - vim.o.foldcolumn = foldcolumn - vim.o.foldenable = foldenable - vim.o.foldminlines = foldminlines - end - - vim.b.current_syntax = "" ---@diagnostic disable-line - M.data.last_buffer = vim.api.nvim_buf_get_name(buf) - end - end - -- end) - end, - - -- remove loaded syntax include and snip region - remove_syntax = function(group, snip) - -- these clears are silent. errors do not matter - -- errors are assumed to come from the functions that call this - local group_remove = string.format("silent! syntax clear @%s", group) - vim.cmd(group_remove) - - local snip_remove = string.format("silent! syntax clear %s", snip) - vim.cmd(snip_remove) - end, - - -- sync regex code blocks - sync_regex_code_blocks = function(buf, regex, from, to) - local current_buf = vim.api.nvim_buf_get_name(buf) - -- only parse from the loaded_code_blocks M, not from the file directly - if M.data.code_block_table[current_buf] == nil then - return - end - local lang_table = M.data.code_block_table[current_buf].loaded_regex - for lang_name, curr_table in pairs(lang_table) do - -- if we got passed a regex, then we need to only parse the right one - if regex ~= nil then - if regex ~= lang_name then - goto continue - end - end - if curr_table.type == "syntax" then - -- sync from code block - - -- for incremental syncing - if from ~= nil then - local found_lang = false - for start_row, end_row in pairs(curr_table.range) do - -- see if the text changes we made included a regex code block - if start_row <= from and end_row >= to then - found_lang = true - end - end - - -- didn't find match from this range of the current language, skip parsing - if found_lang == false then - goto continue - end - end - - local snip = string.format("textSnip%s", string.upper(lang_name)) - local start_marker = string.format("@code %s", lang_name) - -- local end_marker = "@end" - local regex_fallback_hl = string.format( - [[ - syntax sync match %s - \ grouphere %s - \ "%s" - ]], - snip, - snip, - start_marker - ) - vim.cmd(string.format("silent! %s", regex_fallback_hl)) - - -- NOTE: this is kept as a just in case - -- sync back from end block - -- regex_fallback_hl = string.format( - -- [[ - -- syntax sync match %s - -- \ groupthere %s - -- \ "%s" - -- ]], - -- snip, - -- snip, - -- end_marker - -- ) - -- TODO check groupthere, a slower process - -- vim.cmd(string.format("silent! %s", regex_fallback_hl)) - -- vim.cmd("syntax sync maxlines=100") - end - ::continue:: - end - end, -} - ----@class down.edit.syntax.Config -M.config = { - -- Performance options for highlighting. - -- - -- These options exhibit the same behaviour as the [`concealer`](@concealer)'s. - performance = { - -- How many lines each "chunk" of a file should take up. - -- - -- When the size of the buffer is greater than this value, - -- the buffer is then broken up into equal chunks and operations - -- are done individually on those chunks. - increment = 1250, - - -- How long the syntax M should wait before starting to conceal - -- the buffer. - timeout = 0, - - -- How long the syntax M should wait before starting to conceal - -- a new chunk. - interval = 500, - - -- The maximum amount of recalculations that take place at a single time. - -- More operations than this count will be dropped. - -- - -- Especially useful when e.g. holding down `x` in a buffer, forcing - -- hundreds of recalculations at a time. - max_debounce = 5, - }, -} - -M.load = function() - M.data.available_languages = utils.get_language_list(false) -end - -M.on = function(event) - M.data.debounce_counters[event.cursor_position[1] + 1] = M.data.debounce_counters - [event.cursor_position[1] + 1] - or 0 - - local function should_debounce() - return M.data.debounce_counters[event.cursor_position[1] + 1] - >= M.config.performance.max_debounce - end - - if - event.type == "autocommands.events.bufenter" and event.content.markdown - then - local buf = event.buffer - - local line_count = vim.api.nvim_buf_line_count(buf) - - if line_count < M.config.performance.increment then - M.data.check_code_block_type(buf, false) - M.data.trigger_highlight_regex_code_block(buf, false, false) - else - local block_current = - math.floor(event.cursor_position[1] / M.config.performance.increment) - - local function trigger_syntax_for_block(block) - local line_begin = block == 0 and 0 - or block * M.config.performance.increment - 1 - local line_end = math.min( - block * M.config.performance.increment - + M.config.performance.increment - - 1, - line_count - ) - - M.data.check_code_block_type(buf, false, line_begin, line_end) - M.data.trigger_highlight_regex_code_block( - buf, - false, - false, - line_begin, - line_end - ) - end - - trigger_syntax_for_block(block_current) - - local block_bottom, block_top = block_current - 1, block_current + 1 - - local timer = vim.loop.new_timer() - - timer:start( - M.config.performance.timeout, - M.config.performance.interval, - vim.schedule_wrap(function() - local block_bottom_valid = block_bottom == 0 - or (block_bottom * M.config.performance.increment - 1 >= 0) - local block_top_valid = block_top * M.config.performance.increment - 1 - < line_count - - if - not vim.api.nvim_buf_is_loaded(buf) - or (not block_bottom_valid and not block_top_valid) - then - timer:stop() - return - end - - if block_bottom_valid then - trigger_syntax_for_block(block_bottom) - block_bottom = block_bottom - 1 - end - - if block_top_valid then - trigger_syntax_for_block(block_top) - block_top = block_top + 1 - end - end) - ) - end - - vim.api.nvim_buf_attach(buf, false, { - on_lines = function(_, cur_buf, _, start, _end) - if buf ~= cur_buf then - return true - end - - if should_debounce() then - return - end - - M.data.last_change.active = true - - local mode = vim.api.nvim_get_mode().mode - - if mode ~= "i" then - M.data.debounce_counters[event.cursor_position[1] + 1] = M.data.debounce_counters - [event.cursor_position[1] + 1] - + 1 - - schedule(function() - local new_line_count = vim.api.nvim_buf_line_count(buf) - - -- Sometimes occurs with one-line undos - if start == _end then - _end = _end + 1 - end - - if new_line_count > line_count then - _end = _end + (new_line_count - line_count - 1) - end - - line_count = new_line_count - - vim.schedule(function() - M.data.debounce_counters[event.cursor_position[1] + 1] = M.data.debounce_counters - [event.cursor_position[1] + 1] - - 1 - end) - end) - else - if M.data.largest_change_start == -1 then - M.data.largest_change_start = start - end - - if M.data.largest_change_end == -1 then - M.data.largest_change_end = _end - end - - M.data.largest_change_start = start - < M.data.largest_change_start - and start - or M.data.largest_change_start - M.data.largest_change_end = _end - > M.data.largest_change_end - and _end - or M.data.largest_change_end - end - end, - }) - elseif event.type == "autocommands.events.insertleave" then - if should_debounce() then - return - end - - schedule(function() - if - not M.data.last_change.active - or M.data.largest_change_end == -1 - then - M.data.check_code_block_type( - event.buffer, - false - -- M.data.last_change.line, - -- M.data.last_change.line + 1 - ) - M.data.trigger_highlight_regex_code_block( - event.buffer, - false, - true, - M.data.last_change.line, - M.data.last_change.line + 1 - ) - else - M.data.check_code_block_type( - event.buffer, - false, - M.data.last_change.line, - M.data.last_change.line + 1 - ) - M.data.trigger_highlight_regex_code_block( - event.buffer, - false, - true, - M.data.largest_change_start, - M.data.largest_change_end - ) - end - - M.data.largest_change_start, M.data.largest_change_end = - -1, -1 - end) - elseif event.type == "autocommands.events.vimleavepre" then - M.data.disable_deferred_updates = true - elseif event.type == "autocommands.events.colorscheme" then - M.data.trigger_highlight_regex_code_block(event.buffer, true, false) - end -end - -M.subscribed = { - autocommands = { - bufenter = true, - colorscheme = true, - insertleave = true, - vimleavepre = true, - }, -} - -return M diff --git a/lua/down/mod/edit/link/parse/init.lua b/lua/down/mod/lsp/README.md similarity index 100% rename from lua/down/mod/edit/link/parse/init.lua rename to lua/down/mod/lsp/README.md diff --git a/lua/down/mod/lsp/init.lua b/lua/down/mod/lsp/init.lua index c72b8a8..e1f5e83 100644 --- a/lua/down/mod/lsp/init.lua +++ b/lua/down/mod/lsp/init.lua @@ -19,8 +19,6 @@ end Lsp.load = function() -- local autocmd = Lsp.data.ft('*.{md,dn,dd}') local autocmd = Lsp.data.ft('markdown') - local autocmd = Lsp.data.ft('docdown') - local autocmd = Lsp.data.ft('down') -- local autocmd = Lsp.data.ft('*.dd') -- local autocmd = Lsp.data.ft('*.dn') Lsp.required['cmd'].add_commands_from_table({ diff --git a/lua/down/mod/parse/init.lua b/lua/down/mod/parse/init.lua index 9094547..1fd9525 100644 --- a/lua/down/mod/parse/init.lua +++ b/lua/down/mod/parse/init.lua @@ -1,3 +1,25 @@ local P = require 'down.mod'.create('parse') +P.setup = function() + return { + loaded = true, + requires = { + 'tool.treesitter' + } + } +end + +P.load = function() +end + +---@class down.mod.parse.Config +P.config = { + +} + +---@class down.mod.parse.Data +P.data = { + +} + return P diff --git a/lua/down/mod/ui/calendar/day/init.lua b/lua/down/mod/ui/calendar/day/init.lua index 5ee8e61..c7279e9 100644 --- a/lua/down/mod/ui/calendar/day/init.lua +++ b/lua/down/mod/ui/calendar/day/init.lua @@ -1,3 +1,24 @@ +---@class down.mod.ui.calendar.Day: down.Mod local D = require("down.mod").new("ui.calendar.day") +---@return down.mod.Setup +D.setup = function() + return { ---@type down.mod.Setup + loaded = true, + requires = { + + } + } +end + +---@class down.mod.ui.calendar.day.Data +D.data = { + +} + +---@class down.mod.ui.calendar.day.Config +D.config = { + +} + return D diff --git a/lua/down/mod/ui/calendar/time/init.lua b/lua/down/mod/ui/calendar/time/init.lua index f60d953..7218010 100644 --- a/lua/down/mod/ui/calendar/time/init.lua +++ b/lua/down/mod/ui/calendar/time/init.lua @@ -1,4 +1,5 @@ local T = require("down.mod").new("ui.calendar.time") + function T.setup() return { laoded = true diff --git a/lua/down/mod/workspace/init.lua b/lua/down/mod/workspace/init.lua index d9a4da2..8a7ea1c 100644 --- a/lua/down/mod/workspace/init.lua +++ b/lua/down/mod/workspace/init.lua @@ -1,4 +1,5 @@ local Path = require('pathlib') +local Event = require('down.event') local config = require 'down.config' local log = require 'down.util.log' local util = require 'down.mod.workspace.util' @@ -14,10 +15,9 @@ local M = mod.new('workspace') ---@class down.mod.workspace.Config M.config = { + --- default workspace default = 'default', - -- The list of active down workspaces. - -- There is always an inbuilt workspace called `default`, whose loc is - -- set to the Neovim current working directory on boot. + --- List of workspaces workspaces = { default = vim.fn.getcwd(0), cwd = vim.fn.getcwd(0), @@ -103,8 +103,8 @@ M.data = { if not w then return end - for path in w:fs_iterdir(true, 20) do - if path:is_file(true) and path:suffix() == '.md' then + for p in w:fs_iterdir(true, 20) do + if p:is_file(true) and path:suffix() == '.md' then table.insert(res, path) end end @@ -241,10 +241,10 @@ M.data = { end -- Broadcast the wschanged event with all the necessary information - mod.broadcast( + Event.broadcast_to( assert( - mod.new_event(M, 'workspace.events.wschanged', { old = current_ws, new = new_workspace }) - ) + Event.new(M, 'workspace.events.wschanged', { old = current_ws, new = new_workspace }) + ), mod.mods ) return true @@ -263,7 +263,7 @@ M.data = { -- Set the new workspace and its path accordingly M.config.workspaces[wsname] = wspath -- Broadcast the wsadded event with the newly added workspace as the body - mod.broadcast(assert(mod.new_event(M, 'workspace.events.wsadded', { wsname, wspath }))) + mod.broadcast(assert(Event.new(M, 'workspace.events.wsadded', { wsname, wspath }))) -- Sync autocompletions so the user can see the new workspace M.data.sync() @@ -331,28 +331,28 @@ M.data = { select = function(prompt, fmt, fn) local workspaces = M.data.get_workspaces() local format = fmt - or function(item) - local current = M.data.get_current_workspace() - if item == current then - return '• ' .. item + or function(item) + local current = M.data.get_current_workspace() + if item == current then + return '• ' .. item + end + return item end - return item - end local func = fn - or function(item, idx) - local current = M.data.get_current_workspace() - print(item, idx) - if item == current then - utils.notify('Already in workspace ' .. current) - print(item, idx) - M.data.open_workspace(item) - else + or function(item, idx) + local current = M.data.get_current_workspace() print(item, idx) - M.data.set_workspace(item) - M.data.open_workspace(item) - utils.notify('Workspace set to ' .. item) + if item == current then + utils.notify('Already in workspace ' .. current) + print(item, idx) + M.data.open_workspace(item) + else + print(item, idx) + M.data.set_workspace(item) + M.data.open_workspace(item) + utils.notify('Workspace set to ' .. item) + end end - end return vim.ui.select(vim.tbl_keys(workspaces), { prompt = prompt or 'Select workspace', format_items = format, @@ -404,7 +404,7 @@ M.data = { -- Broadcast file creation event local bufnr = M.data.get_file_bufnr(destination:tostring()) mod.broadcast( - assert(mod.new_event(M, 'workspace.events.file_created', { buffer = bufnr, opts = opts })) + assert(Event.new(M, 'workspace.events.file_created', { buffer = bufnr, opts = opts })) ) if not opts.no_open then @@ -633,7 +633,7 @@ M.handle = function(event) local new_workspace = M.data.get_workspace(event.body[1]) if not new_workspace then - new_workspace = M.data.select() + M.data.select() end utils.notify('New workspace: ' .. event.body[1] .. ' -> ' .. new_workspace) @@ -661,10 +661,10 @@ end ---@class down.mod.workspace.Events M.events = { - wschanged = mod.define_event(M, 'wschanged'), - wsadded = mod.define_event(M, 'wsadded'), - wscache_empty = mod.define_event(M, 'wscache_empty'), - file_created = mod.define_event(M, 'file_created'), + wschanged = Event.define(M, 'wschanged'), + wsadded = Event.define(M, 'wsadded'), + wscache_empty = Event.define(M, 'wscache_empty'), + file_created = Event.define(M, 'file_created'), } ---@class down.mod.workspace.Subscribed diff --git a/lua/down/types/context.lua b/lua/down/types/context.lua index 8711220..91cad50 100644 --- a/lua/down/types/context.lua +++ b/lua/down/types/context.lua @@ -8,9 +8,11 @@ --- The context of an in-file object. --- @class (exact) down.Context context of in-file object ---- @field public loc? down.Position location ---- @field public root? down.Id root node in file scope ---- @field public parent? down.Id parent node in file scope +--- @field public position? down.Position location +--- @field public buf? number +--- @field public win? number +--- @field public file? down.Id root node in file scope +--- @field public dir? down.Id root node in file scope --- @field public scope? down.Scope in file scope --- --- The scope of an entity. diff --git a/lua/down/types/data.lua b/lua/down/types/data.lua deleted file mode 100644 index cb634f2..0000000 --- a/lua/down/types/data.lua +++ /dev/null @@ -1,20 +0,0 @@ ---- @meta down.types.data ---- @brief Provides core data types ---- @version <5.2,JIT ---- ---- @brief down.data ---- ---- The important store value object ---- @class (exact) down.data.Map: { [K]?: V } # store data ---- ---- The important store value object ---- @class (exact) down.data.NestedMap: { [K1]?: { [K2]?: V } } # store data ---- ---- The important store value object ---- @class (exact) down.data.NestedTable: { [K1]: table } # store data ---- ---- The important store value object ---- @class (exact) down.data.Map: { [K]: V } # store data ---- ---- The important store value object ---- @class (exact) down.data.List: { [integer]: V } # store data diff --git a/lua/down/types/event.lua b/lua/down/types/event.lua index 4b0af2f..36b34bc 100644 --- a/lua/down/types/event.lua +++ b/lua/down/types/event.lua @@ -1 +1,33 @@ ---@meta down.types.event +--- +--- @class (exact) down.Event +--- @field type string The type of the event. Exists in the format of `category.name`. +--- @field topic string The type of the event. Exists in the format of `category.name`. +--- @field split string[] The event type, just split on every `.` character, e.g. `{ "category", "name" }`. +--- @field body? table|any The content of the event. The data found here is specific to each individual event. Can be thought of as the payload. +--- @field ref string The name of the init that triggered the event. +--- @field broadcast boolean Whether the event was broadcast to all mod. `true` is so, `false` if the event was specifically sent to a single recipient. +--- @field position { [1]: number, [2]: number } The position of the cursor at the moment of broadcasting the event. +--- @field file string The name of the file that the user was in at the moment of broadcasting the event. +--- @field dir string The name of the file that the user was in at the moment of broadcasting the event. +--- @field line string The content of the line the user was editing at the moment of broadcasting the event. +--- @field buf number The buffer ID of the buffer the user was in at the moment of broadcasting the event. +--- @field win number The window ID of the window the user was in at the moment of broadcasting the event. +--- @field mode string The mode Neovim was in at the moment of broadcasting the event. +--- @field broadcast fun(self: down.Event) +--- @field new fun(m: down.Mod.Mod, type: string, body: table|string, ev?: table): down.Event? +--- @field send fun(self: down.Event, recipient: down.Mod.Mod[]) +--- @field handle fun(self: down.Event) +--- @field define fun(module: down.Mod.Mod, type: string): down.Event +--- @field context? down.Context +--- +--- +--- @class (exact) down.mod.Events: { +--- [string]: down.Event +--- } +--- +--- @class (exact) down.mod.Subscribed: { +--- [string]: { +--- [string]: boolean +--- } +--- } diff --git a/lua/down/types/init.lua b/lua/down/types/init.lua index 2c70e52..4d2ec6c 100644 --- a/lua/down/types/init.lua +++ b/lua/down/types/init.lua @@ -189,18 +189,4 @@ --- @see down.Setup --- --- ---- @class (exact) down.Event ---- @field type string The type of the event. Exists in the format of `category.name`. ---- @field split string[] The event type, just split on every `.` character, e.g. `{ "category", "name" }`. ---- @field body? table|any The content of the event. The data found here is specific to each individual event. Can be thought of as the payload. ---- @field ref string The name of the init that triggered the event. ---- @field broadcast boolean Whether the event was broadcast to all mod. `true` is so, `false` if the event was specifically sent to a single recipient. ---- @field position { [1]: number, [2]: number } The position of the cursor at the moment of broadcasting the event. ---- @field file string The name of the file that the user was in at the moment of broadcasting the event. ---- @field dir string The name of the file that the user was in at the moment of broadcasting the event. ---- @field line string The content of the line the user was editing at the moment of broadcasting the event. ---- @field buf number The buffer ID of the buffer the user was in at the moment of broadcasting the event. ---- @field win number The window ID of the window the user was in at the moment of broadcasting the event. ---- @field mode Mode The mode Neovim was in at the moment of broadcasting the event. ---- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- diff --git a/lua/down/types/mod.lua b/lua/down/types/mod.lua index 9229623..9a391c0 100644 --- a/lua/down/types/mod.lua +++ b/lua/down/types/mod.lua @@ -1,10 +1,12 @@ ---@meta down.types.mod --- +--- @alias down.mod.Handler fun(event: down.Event) +--- +--- @alias down.mod.SetupFun fun(): down.mod.Setup +--- --- @class (exact) down.Mod ---- @field public [string]? down.mod.Data --- @field hook? fun(arguments?: string) A user-defined function that is invoked whenever down starts up. May be used to e.g. set custom keybindings. --- @field config? down.mod.Config The config for the mod. ---- @field events? down.mod.Events Describes all information related to events for this mod. --- @field import? table Imported submod of the given mod. Contrary to `required`, which only exposes the public API of a mod, imported mod can be accessed in their entirety. --- @field cmds? fun() Function that adds all the commands for the mod. --- @field opts? fun() Function that adds all the options for the mod. @@ -22,6 +24,9 @@ --- @field replaced? boolean If `true`, this means the mod is a replacement for a base mod. This flag is set automatically whenever `setup().replaces` is set to a value. --- @field handle fun(event: down.Event) A callback that is invoked any time an event the mod has subscribed to has fired. --- @field test? fun() Function that is invoked when the mod is being tested. +--- @field public events? down.mod.Events +--- @field public subscribed? down.mod.Events +-- --- @field public [string]? down.mod.Data --- --- @class (exact) down.mod.Setup: { --- [string]?: { [string]?: any }, @@ -29,17 +34,15 @@ --- requires?: string[], --- replaces?: string, --- merge?: boolean, ---- wants?: string[] } --- @field public [string]? any --- ---- @class (exact) down.mod.Events ---- @field defined? { [string]: down.Event } Lists all events by this init. ---- @field subscribed? { [string]: { [string]: boolean } } Lists the events that the init is subscribed to. ---- @field public [string]? down.Event +--- @class (exact) down.mod.Events: { [string]: down.Event } --- --- The entire mod configuration --- @alias down.config.Mod --- | down.mod.Lsp +--- | down.mod.Code +--- | down.mod.Parse --- | down.mod.Edit --- | down.mod.Data --- | down.mod.Cmd @@ -59,6 +62,21 @@ --- | down.mod.workspace.Config --- | down.mod.note.Config --- | down.mod.ui.Config +--- | down.mod.parse.Config +--- | down.mod.code.Config +--- +--- The entire mod configuration +--- @alias down.Mod.Mod +--- | down.mod.Lsp +--- | down.mod.Code +--- | down.mod.Parse +--- | down.mod.Edit +--- | down.mod.Data +--- | down.mod.Cmd +--- | down.mod.Tool +--- | down.mod.Workspace +--- | down.mod.Note +--- | down.mod.Ui --- --- @alias down.Mod.Data --- | down.mod.lsp.Data @@ -69,6 +87,8 @@ --- | down.mod.workspace.Data --- | down.mod.note.Data --- | down.mod.ui.Data +--- | down.mod.parse.Data +--- | down.mod.code.Data --- --- @alias down.Mod.Config --- | down.mod.lsp.Config @@ -80,6 +100,8 @@ --- | down.mod.workspace.Config --- | down.mod.note.Config --- | down.mod.ui.Config +--- | down.mod.parse.Config +--- | down.mod.code.Config --- --- The base configuration --- @class (exact) down.config.BaseConfig: { diff --git a/lua/down/util/event/callback.lua b/lua/down/util/event/callback.lua deleted file mode 100644 index 7e47f5f..0000000 --- a/lua/down/util/event/callback.lua +++ /dev/null @@ -1,41 +0,0 @@ ---- @class down.C -local C = { - ---@type table - callback_list = {}, -} - ---- Triggers a new callback to execute whenever an event of the requested type is executed. ---- @param event_name string The full path to the event we want to listen on. ---- @param callback fun(event: down.event, content: table|any) The function to call whenever our event gets triggered. ---- @param content_filter? fun(event: down.event): boolean # A filtering function to test if a certain event meets our expectations. -function C.handle(event_name, callback, content_filter) - -- If the table doesn't exist then create it - C.callback_list[event_name] = C.callback_list[event_name] or {} - -- Insert the callback and content filter - require("table").insert( - C.callback_list[event_name], - { callback, content_filter } - ) -end - ---- Used internally by down to call all C with an event. ---- @param event down.event An event as returned by `mod.new_event()` ---- @see mod.new_event -function C.handle(event) - -- Query the list of registered C - local callback_entry = C.callback_list[event.type] - - -- If the C exist then - if callback_entry then - -- Loop through every callback - for _, callback in ipairs(callback_entry) do - -- If the filter event has not been defined or if the filter returned true then - if not callback[2] or callback[2](event) then - -- Execute the callback - callback[1](event, event.content) - end - end - end -end - -return C diff --git a/lua/down/util/event/init.lua b/lua/down/util/event/init.lua deleted file mode 100644 index e69de29..0000000 diff --git a/lua/down/util/init.lua b/lua/down/util/init.lua index 92ecad3..e0b1a48 100644 --- a/lua/down/util/init.lua +++ b/lua/down/util/init.lua @@ -116,7 +116,7 @@ function U.get_language_shorthands(reverse_lookup) ['c_sharp'] = { 'csharp', 'cs' }, ['clojure'] = { 'clj' }, ['cmake'] = { 'cmake.in' }, - ['coM.handlelisp'] = { 'cl' }, + ['commonlisp'] = { 'cl' }, ['cpp'] = { 'hpp', 'cc', 'hh', 'c++', 'h++', 'cxx', 'hxx' }, ['dockerfile'] = { 'docker' }, ['erlang'] = { 'erl' }, diff --git a/neovim.yml b/neovim.yml new file mode 100644 index 0000000..53daa57 --- /dev/null +++ b/neovim.yml @@ -0,0 +1,5 @@ +--- +base: lua51 +globals: + vim: + any: true diff --git a/note/2024/12/28.md b/note/2024/12/28.md deleted file mode 100644 index e69de29..0000000