From cf961dcd15ea628c47dfe4ec479e1a82038c57fe Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Fri, 5 Jan 2024 19:42:20 +0100 Subject: [PATCH 01/15] docs(neorg/core/config): add LuaCATS annotations --- lua/neorg/core/config.lua | 41 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/lua/neorg/core/config.lua b/lua/neorg/core/config.lua index f265d4b06..abf7f4e1a 100644 --- a/lua/neorg/core/config.lua +++ b/lua/neorg/core/config.lua @@ -1,4 +1,34 @@ --- Grab OS info on startup +--- @brief [[ +--- Defines the configuration table for use throughout Neorg. +--- @brief ]] + +-- TODO(vhyrro): Make `norg_version` and `version` a `Version` class. + +--- @alias OperatingSystem +--- | "windows" +--- | "wsl" +--- | "wsl2" +--- | "mac" +--- | "linux" + +--- @alias neorg.configuration.module { config?: table } + +--- @class (exact) neorg.configuration.user +--- @field lazy_loading boolean Whether to defer loading the Neorg core until after the user has entered a `.norg` file. +--- @field load table A list of modules to load, alongside their configurations. + +--- @class (exact) neorg.configuration +--- @field user_config neorg.configuration.user Stores the configuration provided by the user. +--- @field modules table Acts as a copy of the user's configuration that may be modified at runtime. +--- @field manual boolean? Used if Neorg was manually loaded via `:NeorgStart`. Only applicable when `user_config.lazy_loading` is `true`. +--- @field arguments table A list of arguments provided to the `:NeorgStart` function in the form of `key=value` pairs. Only applicable when `user_config.lazy_loading` is `true`. +--- @field norg_version string The version of the file format to be used throughout Neorg. Used internally. +--- @field version string The version of Neorg that is currently active. Automatically updated by CI on every release. +--- @field os_info OperatingSystem The operating system that Neorg is currently running under. +--- @field pathsep "\\"|"/" The operating system that Neorg is currently running under. + +--- Gets the current operating system. +--- @return OperatingSystem local function get_os_info() local os = vim.loop.os_uname().sysname:lower() @@ -19,11 +49,18 @@ local function get_os_info() end return "linux" end + + error("[neorg]: Unable to determine the currently active operating system!") end local os_info = get_os_info() --- Configuration template +--- Stores the configuration for the entirety of Neorg. +--- This includes not only the user configuration (passed to `setup()`), but also internal +--- variables that describe something specific about the user's hardware. +--- @see neorg.setup +--- +--- @type neorg.configuration local config = { user_config = { lazy_loading = false, From c54789f36b0af72e77eed8d95f22aa24ce9e219c Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Fri, 5 Jan 2024 20:02:07 +0100 Subject: [PATCH 02/15] docs(neorg/core/log): add LuaCATS annotations --- lua/neorg/core/log.lua | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/lua/neorg/core/log.lua b/lua/neorg/core/log.lua index e738da989..46be2875a 100644 --- a/lua/neorg/core/log.lua +++ b/lua/neorg/core/log.lua @@ -9,15 +9,33 @@ local lib = require("neorg.core.lib") --- User configuration section +--- @alias LogLevel +--- | "trace" +--- | "debug" +--- | "info" +--- | "warn" +--- | "error" +--- | "fatal" + +--- @class (exact) neorg.log.configuration +--- @field plugin string Name of the plugin. Prepended to log messages. +--- @field use_console boolean Whether to print the output to Neovim while running. +--- @field highlights boolean Whether highlighting should be used in console (using `:echohl`). +--- @field use_file boolean Whether to write output to a file. +--- @field level LogLevel Any messages above this level will be logged. +--- @field modes ({ name: LogLevel, hl: string, level: number })[] Level configuration. +--- @field float_precision float Can limit the number of decimals displayed for floats. + +--- User configuration section +--- @type neorg.log.configuration local default_config = { - -- Name of the plugin. Prepended to log messages + -- plugin = "neorg", - -- Should print the output to neovim while running + -- use_console = true, - -- Should highlighting be used in console (using echohl) + -- highlights = true, -- Should write to a file @@ -47,8 +65,10 @@ log.get_default_config = function() return default_config end -local unpack = unpack or table.unpack ---@diagnostic disable-line -- TODO: type error workaround +local unpack = unpack or table.unpack +--- @param config neorg.log.configuration +--- @param standalone boolean log.new = function(config, standalone) config = vim.tbl_deep_extend("force", default_config, config) config.plugin = "neorg" -- Force the plugin name to be neorg From 0fd898a7993d5a2da4308e007cae6e71d8662b18 Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Fri, 5 Jan 2024 20:02:28 +0100 Subject: [PATCH 03/15] docs(neorg/core/config): add missing annotations --- lua/neorg/core/config.lua | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lua/neorg/core/config.lua b/lua/neorg/core/config.lua index abf7f4e1a..771980917 100644 --- a/lua/neorg/core/config.lua +++ b/lua/neorg/core/config.lua @@ -14,18 +14,21 @@ --- @alias neorg.configuration.module { config?: table } --- @class (exact) neorg.configuration.user ---- @field lazy_loading boolean Whether to defer loading the Neorg core until after the user has entered a `.norg` file. +--- @field hook? fun(manual: boolean, arguments?: string) A user-defined function that is invoked whenever Neorg starts up. May be used to e.g. set custom keybindings. +--- @field lazy_loading? boolean Whether to defer loading the Neorg core until after the user has entered a `.norg` file. --- @field load table A list of modules to load, alongside their configurations. +--- @field logger? neorg.log.configuration A configuration table for the logger. --- @class (exact) neorg.configuration ---- @field user_config neorg.configuration.user Stores the configuration provided by the user. ---- @field modules table Acts as a copy of the user's configuration that may be modified at runtime. ---- @field manual boolean? Used if Neorg was manually loaded via `:NeorgStart`. Only applicable when `user_config.lazy_loading` is `true`. --- @field arguments table A list of arguments provided to the `:NeorgStart` function in the form of `key=value` pairs. Only applicable when `user_config.lazy_loading` is `true`. +--- @field manual boolean? Used if Neorg was manually loaded via `:NeorgStart`. Only applicable when `user_config.lazy_loading` is `true`. +--- @field modules table Acts as a copy of the user's configuration that may be modified at runtime. --- @field norg_version string The version of the file format to be used throughout Neorg. Used internally. ---- @field version string The version of Neorg that is currently active. Automatically updated by CI on every release. --- @field os_info OperatingSystem The operating system that Neorg is currently running under. --- @field pathsep "\\"|"/" The operating system that Neorg is currently running under. +--- @field started boolean Set to `true` when Neorg is fully initialized. +--- @field user_config neorg.configuration.user Stores the configuration provided by the user. +--- @field version string The version of Neorg that is currently active. Automatically updated by CI on every release. --- Gets the current operating system. --- @return OperatingSystem @@ -80,6 +83,9 @@ local config = { os_info = os_info, pathsep = os_info == "windows" and "\\" or "/", + + hook = nil, + started = false, } return config From 76fbd94ff15f76249eab181b67f792dc27389604 Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Fri, 5 Jan 2024 20:02:41 +0100 Subject: [PATCH 04/15] docs(neorg): add LuaCATS annotations --- lua/neorg.lua | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/lua/neorg.lua b/lua/neorg.lua index 36e8ec3fa..ae184ac0d 100644 --- a/lua/neorg.lua +++ b/lua/neorg.lua @@ -1,35 +1,33 @@ ---[[ --- ROOT NEORG FILE --- This file is the beginning of the entire plugin. It's here that everything fires up and starts pumping. ---]] +--- @brief [[ +--- This file marks the beginning of the entire plugin. It's here that everything fires up and starts pumping. +--- @brief ]] --- Require the most important modules local neorg = require("neorg.core") local config, log, modules = neorg.config, neorg.log, neorg.modules ---- This function takes in a user config, parses it, initializes everything and launches neorg if inside a .norg or .org file ----@param cfg table #A table that reflects the structure of config.user_config +--- Initializes Neorg. Parses the supplied user configuration, initializes all selected modules and adds filetype checking for `.norg`. +--- @param cfg table A table that reflects the structure of `config.user_config`. +--- @see config.user_config function neorg.setup(cfg) config.user_config = vim.tbl_deep_extend("force", config.user_config, cfg or {}) - -- Create a new global instance of the neorg logger + -- Create a new global instance of the neorg logger. log.new(config.user_config.logger or log.get_default_config(), true) - -- Make the Neorg filetype detectable through `vim.filetype`. - -- TODO: Make a PR to Neovim to natively support the org and norg - -- filetypes. + -- TODO(vhyrro): Remove this after Neovim 0.10, where `norg` files will be + -- detected automatically. vim.filetype.add({ extension = { norg = "norg", }, }) - -- If the file we have entered has a .norg extension + -- If the file we have entered has a `.norg` extension: if vim.fn.expand("%:e") == "norg" or not config.user_config.lazy_loading then - -- Then boot up the environment + -- Then boot up the environment. neorg.org_file_entered(false) else - -- Else listen for a BufReadPost event and fire up the Neorg environment + -- Else listen for a BufReadPost event for `.norg` files and fire up the Neorg environment. vim.cmd([[ autocmd BufAdd *.norg ++once :lua require('neorg').org_file_entered(false) command! -nargs=* NeorgStart delcommand NeorgStart | lua require('neorg').org_file_entered(true, ) @@ -45,8 +43,8 @@ function neorg.setup(cfg) end --- This function gets called upon entering a .norg file and loads all of the user-defined modules. ----@param manual boolean #If true then the environment was kickstarted manually by the user ----@param arguments string? #A list of arguments in the format of "key=value other_key=other_value" +--- @param manual boolean If true then the environment was kickstarted manually by the user. +--- @param arguments string? A list of arguments in the format of "key=value other_key=other_value". function neorg.org_file_entered(manual, arguments) -- Extract the module list from the user config local module_list = config.user_config and config.user_config.load or {} @@ -124,7 +122,7 @@ function neorg.org_file_entered(manual, arguments) end --- Returns whether or not Neorg is loaded ----@return boolean +--- @return boolean function neorg.is_loaded() return config.started end From e11277711f52279676dcc72b232434696aea3065 Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Fri, 5 Jan 2024 21:33:46 +0100 Subject: [PATCH 05/15] docs(neorg/core/lib): add LuaCATS annotations --- lua/neorg/core/lib.lua | 232 +++++++++++++++++++++-------------------- 1 file changed, 119 insertions(+), 113 deletions(-) diff --git a/lua/neorg/core/lib.lua b/lua/neorg/core/lib.lua index 8ef2378ff..0af14fd00 100644 --- a/lua/neorg/core/lib.lua +++ b/lua/neorg/core/lib.lua @@ -1,14 +1,9 @@ -local lib = { - -- TODO: Are the mod functions used anywhere? - mod = { --- Modifiers for the `map` function - exclude = {}, --- Filtering modifiers that exclude certain elements from a table - }, -} - ---- Returns the item that matches the first item in statements ----@param value any #The value to compare against ----@param compare? function #A custom comparison function ----@return function #A function to invoke with a table of potential matches +local lib = {} + +--- Returns the item that matches the first item in statements. +--- @param value any The value to compare against. +--- @param compare? fun(lhs: any, rhs: any): boolean A custom comparison function. +--- @return fun(statements: table): any # A function to invoke with a table of potential matches. function lib.match(value, compare) -- Returning a function allows for such syntax: -- match(something) { ..matches.. } @@ -74,11 +69,12 @@ function lib.match(value, compare) end end ---- Wrapped around `match()` that performs an action based on a condition ----@param comparison boolean #The comparison to perform ----@param when_true function|any #The value to return when `comparison` is true ----@param when_false function|any #The value to return when `comparison` is false ----@return any #The value that either `when_true` or `when_false` returned +--- Wrapped around `match()` that performs an action based on a condition. +--- @param comparison boolean The comparison to perform. +--- @param when_true function|any The value to return when `comparison` is true. +--- @param when_false function|any The value to return when `comparison` is false. +--- @return any # The value that either `when_true` or `when_false` returned. +--- @see neorg.core.lib.match function lib.when(comparison, when_true, when_false) if type(comparison) ~= "boolean" then comparison = (comparison ~= nil) @@ -90,12 +86,13 @@ function lib.when(comparison, when_true, when_false) }) end ---- Maps a function to every element of a table --- The function can return a value, in which case that specific element will be assigned --- the return value of that function. ----@param tbl table #The table to iterate over ----@param callback function #The callback that should be invoked on every iteration ----@return table #A modified version of the original `tbl`. +--- Maps a function to every element of a table. +--- The function can return a value, in which case that specific element will be assigned +--- the return value of that function. +--- @generic K, V +--- @param tbl table The table to iterate over +--- @param callback fun(key: K, value: V, tbl: table): any? The callback that should be invoked on every iteration +--- @return table # A modified version of the original `tbl`. function lib.map(tbl, callback) local copy = vim.deepcopy(tbl) @@ -111,10 +108,10 @@ function lib.map(tbl, callback) end --- Iterates over all elements of a table and returns the first value returned by the callback. ----@param tbl table #The table to iterate over ----@param callback function #The callback function that should be invoked on each iteration. ---- Can return a value in which case that value will be returned from the `filter()` call. ----@return any|nil #The value returned by `callback`, if any +--- @generic K, V +--- @param tbl table The table to iterate over. +--- @param callback fun(key: K, value: V): V? The callback function that should be invoked on each iteration. Can return a value in which case that value will be returned from the `filter()` call. +--- @return V? # The value returned by `callback`, if any. function lib.filter(tbl, callback) for k, v in pairs(tbl) do local cb = callback(k, v) @@ -125,10 +122,11 @@ function lib.filter(tbl, callback) end end ---- Finds any key in an array ----@param tbl any #An array of values to iterate over ---@diagnostic disable-line -- TODO: type error workaround ----@param element any #The item to find ----@return any|nil #The found value or `nil` if nothing could be found +--- Finds any key in an array. +--- @generic V +--- @param tbl { [number]: V } An array of values to iterate over. +--- @param element V The item to find. +--- @return V? # The found value or `nil` if nothing could be found. function lib.find(tbl, element) return lib.filter(tbl, function(key, value) if value == element then @@ -138,9 +136,9 @@ function lib.find(tbl, element) end --- Inserts a value into a table if it doesn't exist, else returns the existing value. ----@param tbl table #The table to insert into ----@param value number|string #The value to insert ----@return any #The item to return +--- @param tbl table The table to insert into. +--- @param value any The value to insert. +--- @return `value` # The item to return. function lib.insert_or(tbl, value) local item = lib.find(tbl, value) @@ -150,10 +148,10 @@ function lib.insert_or(tbl, value) end)() end ---- Picks a set of values from a table and returns them in an array ----@param tbl table #The table to extract the keys from ----@param values any array[string] #An array of strings, these being the keys you'd like to extract ---@diagnostic disable-line -- TODO: type error workaround ----@return any array[any] #The picked values from the table ---@diagnostic disable-line -- TODO: type error workaround +--- Picks a set of values from a table and returns them in an array. +--- @param tbl table The table to extract the keys from. +--- @param values string[] An array of strings, these being the keys you'd like to extract. +--- @return any # The picked values from the table. function lib.pick(tbl, values) local result = {} @@ -167,9 +165,9 @@ function lib.pick(tbl, values) end --- Tries to extract a variable in all nesting levels of a table. ----@param tbl table #The table to traverse ----@param value any #The value to look for - note that comparison is done through the `==` operator ----@return any|nil #The value if it was found, else nil +--- @param tbl table The table to traverse. +--- @param value any The value to look for - note that comparison is done through the `==` operator. +--- @return any? # The value if it was found, else nil. function lib.extract(tbl, value) local results = {} @@ -186,10 +184,10 @@ function lib.extract(tbl, value) return results end ---- Wraps a conditional "not" function in a vim.tbl callback ----@param cb function #The function to wrap ----@vararg ... #The arguments to pass to the wrapped function ----@return function #The wrapped function in a vim.tbl callback +--- Wraps a conditional "not" function in a callback. +--- @param cb function The function to wrap +--- @param ... any The arguments to pass to the wrapped function +--- @return function # The wrapped function in a callback function lib.wrap_cond_not(cb, ...) local params = { ... } return function(v) @@ -197,10 +195,10 @@ function lib.wrap_cond_not(cb, ...) end end ---- Wraps a conditional function in a vim.tbl callback ----@param cb function #The function to wrap ----@vararg ... #The arguments to pass to the wrapped function ----@return function #The wrapped function in a vim.tbl callback +--- Wraps a conditional function in a callback. +--- @param cb function The function to wrap. +--- @param ... any The arguments to pass to the wrapped function. +--- @return function # The wrapped function in a callback. function lib.wrap_cond(cb, ...) local params = { ... } return function(v) @@ -208,10 +206,11 @@ function lib.wrap_cond(cb, ...) end end ---- Wraps a function in a callback ----@param function_pointer function #The function to wrap ----@vararg ... #The arguments to pass to the wrapped function ----@return function #The wrapped function in a callback +--- Wraps a function in a callback. +--- @generic T: function, A +--- @param function_pointer T The function to wrap. +--- @param ... A The arguments to pass to the wrapped function. +--- @return fun(...: A): T # The wrapped function in a callback. function lib.wrap(function_pointer, ...) local params = { ... } @@ -219,7 +218,7 @@ function lib.wrap(function_pointer, ...) local prev = function_pointer -- luacheck: push ignore - function_pointer = function(...) ---@diagnostic disable-line -- TODO: type error workaround + function_pointer = function() return prev, unpack(params) end -- luacheck: pop @@ -230,10 +229,11 @@ function lib.wrap(function_pointer, ...) end end ---- Repeats an arguments `index` amount of times ----@param value any #The value to repeat ----@param index number #The amount of times to repeat the argument ----@return ... #An expanded vararg with the repeated argument +--- Repeats an arguments `index` amount of times. +--- @generic T +--- @param value T The value to repeat. +--- @param index number The amount of times to repeat the argument. +--- @return T ... # An expanded vararg with the repeated argument. function lib.reparg(value, index) if index == 1 then return value @@ -243,26 +243,27 @@ function lib.reparg(value, index) end --- Lazily concatenates a string to prevent runtime errors where an object may not exist --- Consider the following example: --- --- lib.when(str ~= nil, str .. " extra text", "") --- --- This would fail, simply because the string concatenation will still be evaluated in order --- to be placed inside the variable. You may use: --- --- lib.when(str ~= nil, lib.lazy_string_concat(str, " extra text"), "") --- --- To mitigate this issue directly. ---- @vararg string #An unlimited number of strings ----@return string #The result of all the strings concatenateA. +--- Consider the following example: +--- +--- lib.when(str ~= nil, str .. " extra text", "") +--- +--- This would fail, simply because the string concatenation will still be evaluated in order +--- to be placed inside the variable. You may use: +--- +--- lib.when(str ~= nil, lib.lazy_string_concat(str, " extra text"), "") +--- +--- To mitigate this issue directly. +--- @param ... string An unlimited number of strings. +--- @return string The result of all the strings concatenated. function lib.lazy_string_concat(...) return table.concat({ ... }) end --- Converts an array of values to a table of keys ----@param values string[]|number[] #An array of values to store as keys ----@param default any #The default value to assign to all key pairs ----@return table #The converted table +--- @generic K, V +--- @param values K[] An array of values to store as keys. +--- @param default V The default value to assign to all key pairs. +--- @return { [K]: V } # The converted table. function lib.to_keys(values, default) local ret = {} @@ -274,9 +275,9 @@ function lib.to_keys(values, default) end --- Constructs a new key-pair table by running a callback on all elements of an array. ----@param keys string[] #A string array with the keys to iterate over ----@param cb function #A function that gets invoked with each key and returns a value to be placed in the output table ----@return table #The newly constructed table +--- @param keys string[] A string array with the keys to iterate over. +--- @param cb fun(key: string): any? A function that gets invoked with each key and returns a value to be placed in the output table. +--- @return table # The newly constructed table. function lib.construct(keys, cb) local result = {} @@ -287,10 +288,10 @@ function lib.construct(keys, cb) return result end ---- If `val` is a function, executes it with the desired arguments, else just returns `val` ----@param val any|function #Either a function or any other value ----@vararg any #Potential arguments to give `val` if it is a function ----@return any #The returned evaluation of `val` +--- If `val` is a function, executes it with the desired arguments, else just returns `val`. +--- @param val function|any Either a function or any other value. +--- @param ... any Potential arguments to give `val` if it is a function. +--- @return any # The returned evaluation of `val`. function lib.eval(val, ...) if type(val) == "function" then return val(...) @@ -300,14 +301,18 @@ function lib.eval(val, ...) end --- Extends a list by constructing a new one vs mutating an existing --- list in the case of `vim.list_extend` +--- list in the case of `vim.list_extend`. +--- @param list any[] The list to extend. +--- @param ... any[] A set of lists to expand the current list by. +--- @return any[] # The lists concatenated to each other. function lib.list_extend(list, ...) return list and { unpack(list), unpack(lib.list_extend(...)) } or {} end --- Converts a table with `key = value` pairs to a `{ key, value }` array. ----@param tbl_with_keys table #A table with key-value pairs ----@return any array #An array of `{ key, value }` pairs. ---@diagnostic disable-line -- TODO: type error workaround +--- @generic K, V +--- @param tbl_with_keys table A table with key-value pairs. +--- @return { [number]: { [0]: K, [1]: V } } # An array of `{ key, value }` pairs. function lib.unroll(tbl_with_keys) local res = {} @@ -319,10 +324,11 @@ function lib.unroll(tbl_with_keys) end --- Works just like pcall, except returns only a single value or nil (useful for ternary operations --- which are not possible with a function like `pcall` that returns two values). ----@param func function #The function to invoke in a protected environment ----@vararg any #The parameters to pass to `func` ----@return any|nil #The return value of the executed function or `nil` +--- which are not possible with a function like `pcall` that returns two values). +--- @generic T +--- @param func fun(...: any): T The function to invoke in a protected environment. +--- @param ... any The parameters to pass to `func`. +--- @return T? # The return value of the executed function or `nil`. function lib.inline_pcall(func, ...) local ok, ret = pcall(func, ...) @@ -333,10 +339,10 @@ function lib.inline_pcall(func, ...) -- return nil end ---- Perform a backwards search for a character and return the index of that character ----@param str string #The string to search ----@param char string #The substring to search for ----@return number|nil #The index of the found substring or `nil` if not found +--- Perform a backwards search for a character and return the index of that character. +--- @param str string The string to search. +--- @param char string The substring to search for. +--- @return number? # The index of the found substring or `nil` if not found. function lib.rfind(str, char) local length = str:len() local found_from_back = str:reverse():find(char) @@ -344,9 +350,9 @@ function lib.rfind(str, char) end --- Ensure that a nested set of variables exists. --- Useful when you want to initialise a chain of nested values before writing to them. ----@param tbl table #The table you want to modify ----@vararg string #A list of indices to recursively nest into. +--- Useful when you want to initialise a chain of nested values before writing to them. +--- @param tbl table The table you want to modify. +--- @param ... string A list of indices to recursively nest into. function lib.ensure_nested(tbl, ...) local ref = tbl or {} @@ -357,8 +363,8 @@ function lib.ensure_nested(tbl, ...) end --- Capitalizes the first letter of each word in a given string. ----@param str string #The string to capitalize ----@return string #The capitalized string. +--- @param str string The string to capitalize. +--- @return string # The capitalized string. function lib.title(str) local result = {} @@ -371,10 +377,10 @@ function lib.title(str) end --- Wraps a number so that it fits within a given range. ----@param value number #The number to wrap ----@param min number #The lower bound ----@param max number #The higher bound ----@return number #The wrapped number, guarantees `min <= value <= max`. +--- @param value number The number to wrap. +--- @param min number The lower bound. +--- @param max number The higher bound. +--- @return number # The wrapped number, guarantees `min <= value <= max`. function lib.number_wrap(value, min, max) local range = max - min + 1 local wrapped_value = ((value - min) % range) + min @@ -386,10 +392,10 @@ function lib.number_wrap(value, min, max) return wrapped_value end ---- Split a path into its components ---- example: /my/cool/path/file.txt --> { my, cool, path, file.txt } ----@param path string #The path to split ----@return table #The path components +--- Split a path into its components. +--- Example: `/my/cool/path/file.txt` --> `{ my, cool, path, file.txt }` +--- @param path string The path to split. +--- @return string[] # The path components. function lib.tokenize_path(path) local tokens = {} for capture in path:gmatch("[^/\\]+") do @@ -399,8 +405,8 @@ function lib.tokenize_path(path) end --- Lazily copy a table-like object. ----@param to_copy table|any #The table to copy. If any other type is provided it will be copied immediately. ----@return table #The copied table +--- @param to_copy table|any The table to copy. If any other type is provided it will be copied immediately. +--- @return table|any # The copied table. function lib.lazy_copy(to_copy) if type(to_copy) ~= "table" then return vim.deepcopy(to_copy) @@ -461,20 +467,20 @@ function lib.lazy_copy(to_copy) }) end ---- Wrapper function to add two values --- This function only takes in one argument because the second value --- to add is provided as a parameter in the callback. ----@param amount number #The number to add ----@return function #A callback adding the static value to the dynamic amount +--- Wrapper function to add two values. +--- This function only takes in one argument because the second value to add is provided as a parameter in the callback. +--- @param amount number The number to add. +--- @return fun(_, value: number): number # A callback adding the static value to the dynamic amount. function lib.mod.add(amount) return function(_, value) return value + amount end end ---- Wrapper function to set a value to another value in a `map` sequence ----@param to any #A static value to set each element of the table to ----@return function #A callback that returns the static value +--- Wrapper function to set a value to another value in a `map` sequence. +--- @generic T +--- @param to T A static value to set each element of the table to. +--- @return fun(): T # A callback that returns the static value function lib.mod.modify(to) return function() return to From 14de1663af6394d73f4d9705711c126ba7122840 Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Fri, 5 Jan 2024 22:11:07 +0100 Subject: [PATCH 06/15] docs(neorg/core/utils): add LuaCATS documentation --- lua/neorg/core/utils.lua | 72 ++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/lua/neorg/core/utils.lua b/lua/neorg/core/utils.lua index a07bef144..1a9f6267d 100644 --- a/lua/neorg/core/utils.lua +++ b/lua/neorg/core/utils.lua @@ -7,16 +7,18 @@ local version = vim.version() -- TODO: Move to a more local scope --- A version agnostic way to call the neovim treesitter query parser --- @param language string # Language to use for the query --- @param query_string string # Query in s-expr syntax ---- @return any # Parsed query +--- @return Query # Parsed query function utils.ts_parse_query(language, query_string) if vim.treesitter.query.parse then return vim.treesitter.query.parse(language, query_string) else - return vim.treesitter.parse_query(language, query_string) ---@diagnostic disable-line -- TODO: type error workaround + ---@diagnostic disable-next-line + return vim.treesitter.parse_query(language, query_string) end end --- An OS agnostic way of querying the current user +--- @return string username function utils.get_username() local current_os = configuration.os_info @@ -33,26 +35,31 @@ function utils.get_username() return "" end ---- Returns an array of strings, the array being a list of languages that Neorg can inject ----@param values boolean #If set to true will return an array of strings, if false will return a key-value table +--- Returns an array of strings, the array being a list of languages that Neorg can inject. +---@param values boolean If set to true will return an array of strings, if false will return a key-value table. +---@return string[]|table function utils.get_language_list(values) local regex_files = {} local ts_files = {} - -- search for regex files in syntax and after/syntax - -- its best if we strip out anything but the ft name + + -- Search for regex files in syntax and after/syntax. + -- Its best if we strip out anything but the ft name. for _, lang in pairs(vim.api.nvim_get_runtime_file("syntax/*.vim", true)) do local lang_name = vim.fn.fnamemodify(lang, ":t:r") table.insert(regex_files, lang_name) end + for _, lang in pairs(vim.api.nvim_get_runtime_file("after/syntax/*.vim", true)) do local lang_name = vim.fn.fnamemodify(lang, ":t:r") table.insert(regex_files, lang_name) end - -- search for available parsers + + -- Search for available parsers for _, parser in pairs(vim.api.nvim_get_runtime_file("parser/*.so", true)) do - local parser_name = vim.fn.fnamemodify(parser, ":t:r") + local parser_name = assert(vim.fn.fnamemodify(parser, ":t:r")) ts_files[parser_name] = true end + local ret = {} for _, syntax in pairs(regex_files) do @@ -66,7 +73,11 @@ function utils.get_language_list(values) return values and vim.tbl_keys(ret) or ret end +--- Gets a list of shorthands for a given language. +--- @param reverse_lookup boolean Whether to create a reverse lookup for the table. +--- @return LanguageList function utils.get_language_shorthands(reverse_lookup) + ---@class LanguageList local langs = { ["bash"] = { "sh", "zsh" }, ["c_sharp"] = { "csharp", "cs" }, @@ -98,11 +109,11 @@ function utils.get_language_shorthands(reverse_lookup) return reverse_lookup and vim.tbl_add_reverse_lookup(langs) or langs end ---- Checks whether Neovim is running at least at a specific version ----@param major number #The major release of Neovim ----@param minor number #The minor release of Neovim ----@param patch number #The patch number (in case you need it) ----@return boolean #Whether Neovim is running at the same or a higher version than the one given +--- Checks whether Neovim is running at least at a specific version. +--- @param major number The major release of Neovim. +--- @param minor number The minor release of Neovim. +--- @param patch number The patch number (in case you need it). +--- @return boolean # Whether Neovim is running at the same or a higher version than the one given. function utils.is_minimum_version(major, minor, patch) if major ~= version.major then return major < version.major @@ -117,16 +128,18 @@ function utils.is_minimum_version(major, minor, patch) end --- Parses a version string like "0.4.2" and provides back a table like { major = , minor = , patch = } ----@param version_string string #The input string ----@return table #The parsed version string, or `nil` if a failure occurred during parsing +--- @param version_string string The input string. +--- @return table? # The parsed version string, or `nil` if a failure occurred during parsing. function utils.parse_version_string(version_string) if not version_string then - return ---@diagnostic disable-line -- TODO: type error workaround + return end -- Define variables that split the version up into 3 slices local split_version, versions, ret = - vim.split(version_string, ".", true), { "major", "minor", "patch" }, { major = 0, minor = 0, patch = 0 } ---@diagnostic disable-line -- TODO: type error workaround + vim.split(version_string, ".", { plain = true }), + { "major", "minor", "patch" }, + { major = 0, minor = 0, patch = 0 } -- If the sliced version string has more than 3 elements error out if #split_version > 3 then @@ -135,7 +148,7 @@ function utils.parse_version_string(version_string) version_string, "failed - too many version numbers provided. Version should follow this layout: .." ) - return ---@diagnostic disable-line -- TODO: type error workaround + return end -- Loop through all the versions and check whether they are valid numbers. If they are, add them to the return table @@ -145,7 +158,7 @@ function utils.parse_version_string(version_string) if not num then log.warn("Invalid version provided, string cannot be converted to integral type.") - return ---@diagnostic disable-line -- TODO: type error workaround + return end ret[ver] = num @@ -155,16 +168,16 @@ function utils.parse_version_string(version_string) return ret end ---- Custom neorg notifications. Wrapper around vim.notify ----@param msg string message to send ----@param log_level integer|nil log level in `vim.log.levels`. +--- Custom Neorg notifications. Wrapper around `vim.notify`. +--- @param msg string Message to send. +--- @param log_level integer? Log level in `vim.log.levels`. function utils.notify(msg, log_level) vim.notify(msg, log_level, { title = "Neorg" }) end --- Opens up an array of files and runs a callback for each opened file. ----@param files string[] #An array of files to open. ----@param callback fun(buffer: integer, filename: string) #The callback to invoke for each file. +--- @param files string[] An array of files to open. +--- @param callback fun(buffer: integer, filename: string) The callback to invoke for each file. function utils.read_files(files, callback) for _, file in ipairs(files) do local bufnr = vim.uri_to_bufnr(vim.uri_from_fname(file)) @@ -195,7 +208,8 @@ function utils.wrap_dotrepeat(event_handler) utils._neorg_is_dotrepeat = false utils.set_operatorfunc(function() if utils._neorg_is_dotrepeat then - local pos = vim.fn.getpos(".") + local pos = assert(vim.fn.getpos(".")) + event.buffer = pos[1] event.cursor_position = { pos[2], pos[3] } end @@ -206,10 +220,10 @@ function utils.wrap_dotrepeat(event_handler) end end ---- Truncate input str to fit inside the col_limit when displayed. Takes non-ascii chars into account. ----@param str string ----@param col_limit integer #str will be cut so that when displayed, the display length does not exceed limit ----@return string #substring of input str +--- Truncate input string to fit inside the `col_limit` when displayed. Takes non-ascii chars into account. +--- @param str string The string to limit. +--- @param col_limit integer `str` will be cut so that when displayed, the display length does not exceed this limit. +--- @return string # Substring of input str function utils.truncate_by_cell(str, col_limit) if str and str:len() == vim.api.nvim_strwidth(str) then return vim.fn.strcharpart(str, 0, col_limit) From 7b4d9059586253f3ce9a27f5dfda392fe8c3446e Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Fri, 5 Jan 2024 22:11:41 +0100 Subject: [PATCH 07/15] docs(neorg/core/log): remove extraneous comments --- lua/neorg/core/log.lua | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lua/neorg/core/log.lua b/lua/neorg/core/log.lua index 46be2875a..9e2aced7e 100644 --- a/lua/neorg/core/log.lua +++ b/lua/neorg/core/log.lua @@ -29,22 +29,16 @@ local lib = require("neorg.core.lib") --- User configuration section --- @type neorg.log.configuration local default_config = { - -- plugin = "neorg", - -- use_console = true, - -- highlights = true, - -- Should write to a file use_file = true, - -- Any messages above this level will be logged. level = "warn", - -- Level configuration modes = { { name = "trace", hl = "Comment", level = vim.log.levels.TRACE }, { name = "debug", hl = "Comment", level = vim.log.levels.DEBUG }, @@ -54,7 +48,6 @@ local default_config = { { name = "fatal", hl = "ErrorMsg", level = 5 }, }, - -- Can limit the number of decimals displayed for floats float_precision = 0.01, } From 81558c0ceba932b90bdc5c6c34ef15a1b5b8d8c2 Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Fri, 5 Jan 2024 22:14:13 +0100 Subject: [PATCH 08/15] chore(neorg/core/lib): fix errors with `lib.mod` --- lua/neorg/core/lib.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lua/neorg/core/lib.lua b/lua/neorg/core/lib.lua index 0af14fd00..91111c6ae 100644 --- a/lua/neorg/core/lib.lua +++ b/lua/neorg/core/lib.lua @@ -467,6 +467,8 @@ function lib.lazy_copy(to_copy) }) end +lib.mod = {} + --- Wrapper function to add two values. --- This function only takes in one argument because the second value to add is provided as a parameter in the callback. --- @param amount number The number to add. @@ -487,6 +489,8 @@ function lib.mod.modify(to) end end +lib.mod.exclude = {} + function lib.mod.exclude.first(func, alt) return function(i, val) return i == 1 and (alt and alt(i, val) or val) or func(i, val) From 25f029d0b5067d0c7f5693e5328ae90cc57dc2ea Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Fri, 5 Jan 2024 23:17:51 +0100 Subject: [PATCH 09/15] docs(neorg/core/modules): create initial LuaCATS annotations for modules --- lua/neorg/core/modules.lua | 154 ++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 71 deletions(-) diff --git a/lua/neorg/core/modules.lua b/lua/neorg/core/modules.lua index 39e571681..0e3bd47aa 100644 --- a/lua/neorg/core/modules.lua +++ b/lua/neorg/core/modules.lua @@ -1,61 +1,82 @@ +--- @brief [[ +--- Base file for modules. +--- This file contains the base implementation for "modules", building blocks of the Neorg environment. +--- @brief ]] + -- TODO: What goes below this line until the next notice used to belong to modules.base -- We need to find a way to make these constructors easier to maintain and more efficient ---[[ --- BASE FILE FOR MODULES --- This file contains the base module implementation ---]] - local callbacks = require("neorg.core.callbacks") local config = require("neorg.core.config") local log = require("neorg.core.log") local utils = require("neorg.core.utils") +--- @class neorg.module.public +--- @field version string Current Norg version that this module supports. + +--- @class (exact) neorg.module.configuration +--- Defines both a public and private configuration for a Neorg module. +--- Public configurations may be tweaked by the user from the `neorg.setup()` function, +--- whereas private configurations are for internal use only. +--- +--- @field custom? table Internal table that tracks the differences (changes) between the default `public` table and the new (altered) `public` table. It contains only the tables that the user has altered in their own configuration. +--- @field public private? table Internal configuration variables that may be tweaked by the developer. +--- @field public public? table Configuration variables that may be tweaked by the user. + +--- @class (exact) neorg.module.events +--- @field defined? { [string]: neorg.event } Lists all events defined by this module. +--- @field subscribed? { [string]: { [string]: boolean } } Lists the events that the module is subscribed to. + +--- @class (exact) neorg.module +--- Defines a module. +--- A module is an object that contains a set of hooks which are invoked by Neorg whenever something in the +--- environment occurs. This can be an event, a simple act of the module being loaded or anything else. +--- @field config? neorg.module.configuration The configuration for the module. +--- @field events? neorg.module.events Describes all information related to events for this module. +--- @field examples? table Contains examples of how to use the modules that users or developers may sift through. +--- @field imported? table Imported submodules of the given module. Contrary to `required`, which only exposes the public API of a module, imported modules can be accessed in their entirety. +--- @field load? fun() Function that is invoked once the module is considered "stable", i.e. after all dependencies are loaded. Perform your main loading routine here. +--- @field name string The name of the module. +--- @field neorg_post_load? fun() Function that is invoked after all modules are loaded. Useful if you want the Neorg environment to be fully set up before performing some task. +--- @field path string The full path to the module (a more verbose version of `name`). May be used in lua's `require()` statements. +--- @field public private? table A convenience table to place all of your private variables that you don't want to expose. +--- @field public public? neorg.module.public Every module can expose any set of information it sees fit through this field. All functions and variables declared in this table will be visiable to any other module loaded. +--- @field required? table Contains the public tables of all modules that were required via the `requires` array provided in the `setup()` function of this module. +--- @field setup? fun(): { success: boolean, requires?: string[], replaces?: string, replace_merge?: boolean, wants?: string[] } Function that is invoked before any other loading occurs. Should perform preliminary startup tasks. +--- @field replaced? boolean If `true`, this means the module is a replacement for a core module. This flag is set automatically whenever `setup().replaces` is set to a value. +--- @field on_event fun(event: neorg.event) A callback that is invoked any time an event the module has subscribed to has fired. + local modules = {} ---- Returns a new Neorg module, exposing all the necessary function and variables ----@param name string #The name of the new module. Make sure this is unique. The recommended naming convention is category.module_name or category.subcategory.module_name ----@param imports? string[] #A list of imports to attach to the module. Import data is requestable via `module.required`. Use paths relative to the current module. +--- Returns a new Neorg module, exposing all the necessary function and variables. +--- @param name string The name of the new module. Make sure this is unique. The recommended naming convention is `category.module_name` or `category.subcategory.module_name`. +--- @param imports? string[] A list of imports to attach to the module. Import data is requestable via `module.required`. Use paths relative to the current module. +--- @return neorg.module function modules.create(name, imports) + ---@type neorg.module local new_module = { - - -- Invoked before any initial loading happens setup = function() return { success = true, requires = {}, replaces = nil, replace_merge = false } end, - -- Invoked after the module has been configured load = function() end, - -- Invoked whenever an event that the module has subscribed to triggers - -- callback function with a "event" parameter on_event = function() end, - -- Invoked after all plugins are loaded neorg_post_load = function() end, - -- The name of the module, note that modules beginning with core are neorg's inbuilt modules name = "core.default", - -- The path of the module, can be used in require() statements path = "modules.core.default.module", - -- A convenience table to place all of your private variables that you don't want to expose here. private = {}, - -- Every module can expose any set of information it sees fit through the public field - -- All functions and variables declared in this table will be visible to any other module loaded public = { - -- Current Norg version that this module supports. - -- Your module will use this version if not specified, but you can override it. - -- Overriding it will mean that your module is only compatible with the overriden Norg revision. - -- E.g: setting version = "1.0.0" will mean that your module requires Norg 1.0.0+ to operate version = config.norg_version, }, - -- Configuration for the module config = { - private = { -- Private module config, cannot be changed by other modules or by the user + private = { --[[ config_option = false, @@ -65,7 +86,7 @@ function modules.create(name, imports) --]] }, - public = { -- Public config, can be changed by modules and the user + public = { --[[ config_option = false, @@ -75,13 +96,9 @@ function modules.create(name, imports) --]] }, - -- This table houses all the changes the user made to the public table, - -- useful for when you want to know exactly what the user tinkered with. - -- Shouldn't be commonly used. custom = {}, }, - -- Event data regarding the current module events = { subscribed = { -- The events that the module is subscribed to --[[ @@ -99,8 +116,6 @@ function modules.create(name, imports) }, }, - -- If you ever require a module through the return value of the setup() function, - -- All of the modules' public APIs will become available here required = { --[[ ["core.test"] = { @@ -114,7 +129,6 @@ function modules.create(name, imports) --]] }, - -- Example bits of code that the user can look through examples = { --[[ a_cool_test = function() @@ -123,12 +137,9 @@ function modules.create(name, imports) --]] }, - -- Imported submodules of the given module. - -- Contrary to `required`, which only exposes the public API of a module, - -- imported modules can be accessed in their entirety. imported = { --[[ - ["my.module.submodule"] = { ... }, + ["my.module.submodule"] = { ... }, --]] }, } @@ -155,8 +166,8 @@ function modules.create(name, imports) end --- Constructs a metamodule from a list of submodules. Metamodules are modules that can autoload batches of modules at once. ----@param name string #The name of the new metamodule. Make sure this is unique. The recommended naming convention is category.module_name or category.subcategory.module_name --- @Param ... (varargs) - a list of module names to load. +--- @param name string The name of the new metamodule. Make sure this is unique. The recommended naming convention is `category.module_name` or `category.subcategory.module_name`. +--- @param ... string A list of module names to load. function modules.create_meta(name, ...) local module = modules.create(name) @@ -212,12 +223,13 @@ end modules.loaded_module_count = 0 --- The table of currently loaded modules +--- @type { [string]: neorg.module } modules.loaded_modules = {} --- Loads and enables a module --- Loads a specified module. If the module subscribes to any events then they will be activated too. ----@param module table #The actual module to load ----@return boolean #Whether the module successfully loaded +--- Loads a specified module. If the module subscribes to any events then they will be activated too. +--- @param module neorg.module The actual module to load. +--- @return boolean # Whether the module successfully loaded. function modules.load_module_from_table(module) log.info("Loading module with name", module.name) @@ -289,7 +301,7 @@ function modules.load_module_from_table(module) if not modules.load_module(required_module) then log.error( "Unable to load wanted module for", - loaded_module.name, + module.name, "- the module didn't load successfully" ) @@ -418,9 +430,9 @@ end --- Unlike `load_module_from_table()`, which loads a module from memory, `load_module()` tries to find the corresponding module file on disk and loads it into memory. -- If the module cannot not be found, attempt to load it off of github (unimplemented). This function also applies user-defined config and keymaps to the modules themselves. -- This is the recommended way of loading modules - `load_module_from_table()` should only really be used by neorg itself. ----@param module_name string #A path to a module on disk. A path seperator in neorg is '.', not '/' ----@param cfg table? #A config that reflects the structure of `neorg.config.user_config.load["module.name"].config` ----@return boolean #Whether the module was successfully loaded +--- @param module_name string A path to a module on disk. A path seperator in neorg is '.', not '/' +--- @param cfg table? A config that reflects the structure of `neorg.config.user_config.load["module.name"].config` +--- @return boolean #Whether the module was successfully loaded function modules.load_module(module_name, cfg) -- Don't bother loading the module from disk if it's already loaded if modules.is_module_loaded(module_name) then @@ -477,8 +489,8 @@ function modules.load_module(module_name, cfg) end --- Has the same principle of operation as load_module_from_table(), except it then sets up the parent module's "required" table, allowing the parent to access the child as if it were a dependency. ----@param module table #A valid table as returned by modules.create() ----@param parent_module string|table #If a string, then the parent is searched for in the loaded modules. If a table, then the module is treated as a valid module as returned by modules.create() +--- @param module table A valid table as returned by modules.create() +--- @param parent_module string|table If a string, then the parent is searched for in the loaded modules. If a table, then the module is treated as a valid module as returned by modules.create() function modules.load_module_as_dependency_from_table(module, parent_module) if modules.load_module_from_table(module) then if type(parent_module) == "string" then @@ -490,9 +502,9 @@ function modules.load_module_as_dependency_from_table(module, parent_module) end --- Normally loads a module, but then sets up the parent module's "required" table, allowing the parent module to access the child as if it were a dependency. ----@param module_name string #A path to a module on disk. A path seperator in neorg is '.', not '/' ----@param parent_module string #The name of the parent module. This is the module which the dependency will be attached to. ----@param cfg table #A config that reflects the structure of neorg.config.user_config.load["module.name"].config +--- @param module_name string A path to a module on disk. A path seperator in neorg is '.', not '/' +--- @param parent_module string The name of the parent module. This is the module which the dependency will be attached to. +--- @param cfg table A config that reflects the structure of neorg.config.user_config.load["module.name"].config function modules.load_module_as_dependency(module_name, parent_module, cfg) if modules.load_module(module_name, cfg) and modules.is_module_loaded(parent_module) then modules.loaded_modules[parent_module].required[module_name] = modules.get_module_config(module_name) @@ -500,7 +512,7 @@ function modules.load_module_as_dependency(module_name, parent_module, cfg) end --- Retrieves the public API exposed by the module ----@param module_name string #The name of the module to retrieve +--- @param module_name string The name of the module to retrieve function modules.get_module(module_name) if not modules.is_module_loaded(module_name) then log.trace("Attempt to get module with name", module_name, "failed - module is not loaded.") @@ -511,7 +523,7 @@ function modules.get_module(module_name) end --- Returns the module.config.public table if the module is loaded ----@param module_name string #The name of the module to retrieve (module must be loaded) +--- @param module_name string The name of the module to retrieve (module must be loaded) function modules.get_module_config(module_name) if not modules.is_module_loaded(module_name) then log.trace("Attempt to get module config with name", module_name, "failed - module is not loaded.") @@ -522,13 +534,13 @@ function modules.get_module_config(module_name) end --- Returns true if module with name module_name is loaded, false otherwise ----@param module_name string #The name of an arbitrary module +--- @param module_name string The name of an arbitrary module function modules.is_module_loaded(module_name) return modules.loaded_modules[module_name] ~= nil end --- Reads the module's public table and looks for a version variable, then converts it from a string into a table, like so: { major = , minor = , patch = } ----@param module_name string #The name of a valid, loaded module. +--- @param module_name string The name of a valid, loaded module. -- @Return struct | nil (if any error occurs) function modules.get_module_version(module_name) -- If the module isn't loaded then don't bother retrieving its version @@ -550,8 +562,8 @@ function modules.get_module_version(module_name) end --- Executes `callback` once `module` is a valid and loaded module, else the callback gets instantly executed. ----@param module_name string #The name of the module to listen for. ----@param callback fun(module_public_table: table) #The callback to execute. +--- @param module_name string The name of the module to listen for. +--- @param callback fun(module_public_table: table) The callback to execute. function modules.await(module_name, callback) if modules.is_module_loaded(module_name) then callback(assert(modules.get_module(module_name))) @@ -576,7 +588,7 @@ end --- The working of this function is best illustrated with an example: -- If type == 'core.some_plugin.events.my_event', this function will return { 'core.some_plugin', 'my_event' } ----@param type string #The full path of a module event +--- @param type string The full path of a module event function modules.split_event_type(type) local start_str, end_str = type:find("%.events%.") @@ -591,8 +603,8 @@ function modules.split_event_type(type) end --- Returns an event template defined in module.events.defined ----@param module table #A reference to the module invoking the function ----@param type string #A full path to a valid event type (e.g. 'core.module.events.some_event') +--- @param module table A reference to the module invoking the function +--- @param type string A full path to a valid event type (e.g. 'core.module.events.some_event') function modules.get_event_template(module, type) -- You can't get the event template of a type if the type isn't loaded if not modules.is_module_loaded(module.name) then @@ -615,8 +627,8 @@ function modules.get_event_template(module, type) end --- Creates a deep copy of the modules.base_event event and returns it with a custom type and referrer ----@param module table #A reference to the module invoking the function ----@param name string #A relative path to a valid event template +--- @param module table A reference to the module invoking the function +--- @param name string A relative path to a valid event template function modules.define_event(module, name) -- Create a copy of the base event and override the values with ones specified by the user @@ -646,11 +658,11 @@ function modules.define_event(module, name) end --- Returns a copy of the event template provided by a module ----@param module table #A reference to the module invoking the function ----@param type string #A full path to a valid event type (e.g. 'core.module.events.some_event') ----@param content any #The content of the event, can be anything from a string to a table to whatever you please ----@param ev? table the original event data ----@return table #New event +--- @param module table A reference to the module invoking the function +--- @param type string A full path to a valid event type (e.g. 'core.module.events.some_event') +--- @param content any The content of the event, can be anything from a string to a table to whatever you please +--- @param ev? table the original event data +--- @return table #New event function modules.create_event(module, type, content, ev) -- Get the module that contains the event local module_name = modules.split_event_type(type)[1] @@ -689,8 +701,8 @@ function modules.create_event(module, type, content, ev) end --- Sends an event to all subscribed modules. The event contains the filename, filehead, cursor position and line content as a bonus. ----@param event table #An event, usually created by modules.create_event() ----@param callback function? #A callback to be invoked after all events have been asynchronously broadcast +--- @param event table An event, usually created by modules.create_event() +--- @param callback function? A callback to be invoked after all events have been asynchronously broadcast function modules.broadcast_event(event, callback) -- Broadcast the event to all modules if not event.split_type then @@ -723,8 +735,8 @@ function modules.broadcast_event(event, callback) end --- Instead of broadcasting to all loaded modules, send_event() only sends to one module ----@param recipient string #The name of a loaded module that will be the recipient of the event ----@param event table #An event, usually created by modules.create_event() +--- @param recipient string The name of a loaded module that will be the recipient of the event +--- @param event table An event, usually created by modules.create_event() function modules.send_event(recipient, event) -- If the recipient is not loaded then there's no reason to send an event to it if not modules.is_module_loaded(recipient) then From 71823d59577c19605bdc14d3dd8331a083d74d21 Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Fri, 5 Jan 2024 23:29:43 +0100 Subject: [PATCH 10/15] docs(neorg/core/modules): add even more LuaCATS annotations --- lua/neorg/core/modules.lua | 49 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/lua/neorg/core/modules.lua b/lua/neorg/core/modules.lua index 0e3bd47aa..49a2cb3c1 100644 --- a/lua/neorg/core/modules.lua +++ b/lua/neorg/core/modules.lua @@ -27,6 +27,8 @@ local utils = require("neorg.core.utils") --- @field defined? { [string]: neorg.event } Lists all events defined by this module. --- @field subscribed? { [string]: { [string]: boolean } } Lists the events that the module is subscribed to. +--- @alias neorg.module.setup { success: boolean, requires?: string[], replaces?: string, replace_merge?: boolean, wants?: string[] } + --- @class (exact) neorg.module --- Defines a module. --- A module is an object that contains a set of hooks which are invoked by Neorg whenever something in the @@ -42,7 +44,7 @@ local utils = require("neorg.core.utils") --- @field public private? table A convenience table to place all of your private variables that you don't want to expose. --- @field public public? neorg.module.public Every module can expose any set of information it sees fit through this field. All functions and variables declared in this table will be visiable to any other module loaded. --- @field required? table Contains the public tables of all modules that were required via the `requires` array provided in the `setup()` function of this module. ---- @field setup? fun(): { success: boolean, requires?: string[], replaces?: string, replace_merge?: boolean, wants?: string[] } Function that is invoked before any other loading occurs. Should perform preliminary startup tasks. +--- @field setup? fun(): neorg.module.setup Function that is invoked before any other loading occurs. Should perform preliminary startup tasks. --- @field replaced? boolean If `true`, this means the module is a replacement for a core module. This flag is set automatically whenever `setup().replaces` is set to a value. --- @field on_event fun(event: neorg.event) A callback that is invoked any time an event the module has subscribed to has fired. @@ -168,6 +170,7 @@ end --- Constructs a metamodule from a list of submodules. Metamodules are modules that can autoload batches of modules at once. --- @param name string The name of the new metamodule. Make sure this is unique. The recommended naming convention is `category.module_name` or `category.subcategory.module_name`. --- @param ... string A list of module names to load. +--- @return neorg.module function modules.create_meta(name, ...) local module = modules.create(name) @@ -210,16 +213,7 @@ end -- TODO: What goes below this line until the next notice used to belong to modules -- We need to find a way to make these functions easier to maintain ---[[ --- NEORG MODULE MANAGER --- This file is responsible for loading, calling and managing modules --- Modules are internal mini-programs that execute on certain events, they build the foundation of Neorg itself. ---]] - ---[[ --- The reason we do not just call this variable modules.loaded_modules.count is because --- someone could make a module called "count" and override the variable, causing bugs. ---]] +--- Tracks the amount of currently loaded modules. modules.loaded_module_count = 0 --- The table of currently loaded modules @@ -240,6 +234,7 @@ function modules.load_module_from_table(module) end -- Invoke the setup function. This function returns whether or not the loading of the module was successful and some metadata. + ---@type neorg.module.setup local loaded_module = module.setup and module.setup() or { success = true, @@ -270,6 +265,7 @@ function modules.load_module_from_table(module) -- If the module wants to replace an already loaded module then we need to create a deepcopy of that old module -- in order to stop it from getting overwritten. --]] + ---@type neorg.module local module_to_replace -- If the return value of module.setup() tells us to hotswap with another module then cache the module we want to replace with @@ -428,11 +424,11 @@ function modules.load_module_from_table(module) end --- Unlike `load_module_from_table()`, which loads a module from memory, `load_module()` tries to find the corresponding module file on disk and loads it into memory. --- If the module cannot not be found, attempt to load it off of github (unimplemented). This function also applies user-defined config and keymaps to the modules themselves. --- This is the recommended way of loading modules - `load_module_from_table()` should only really be used by neorg itself. ---- @param module_name string A path to a module on disk. A path seperator in neorg is '.', not '/' ---- @param cfg table? A config that reflects the structure of `neorg.config.user_config.load["module.name"].config` ---- @return boolean #Whether the module was successfully loaded +--- If the module cannot not be found, attempt to load it off of github (unimplemented). This function also applies user-defined config and keymaps to the modules themselves. +--- This is the recommended way of loading modules - `load_module_from_table()` should only really be used by neorg itself. +--- @param module_name string A path to a module on disk. A path seperator in neorg is '.', not '/'. +--- @param cfg table? A config that reflects the structure of `neorg.config.user_config.load["module.name"].config`. +--- @return boolean # Whether the module was successfully loaded. function modules.load_module(module_name, cfg) -- Don't bother loading the module from disk if it's already loaded if modules.is_module_loaded(module_name) then @@ -489,8 +485,8 @@ function modules.load_module(module_name, cfg) end --- Has the same principle of operation as load_module_from_table(), except it then sets up the parent module's "required" table, allowing the parent to access the child as if it were a dependency. ---- @param module table A valid table as returned by modules.create() ---- @param parent_module string|table If a string, then the parent is searched for in the loaded modules. If a table, then the module is treated as a valid module as returned by modules.create() +--- @param module neorg.module A valid table as returned by modules.create() +--- @param parent_module string|neorg.module If a string, then the parent is searched for in the loaded modules. If a table, then the module is treated as a valid module as returned by modules.create() function modules.load_module_as_dependency_from_table(module, parent_module) if modules.load_module_from_table(module) then if type(parent_module) == "string" then @@ -504,15 +500,16 @@ end --- Normally loads a module, but then sets up the parent module's "required" table, allowing the parent module to access the child as if it were a dependency. --- @param module_name string A path to a module on disk. A path seperator in neorg is '.', not '/' --- @param parent_module string The name of the parent module. This is the module which the dependency will be attached to. ---- @param cfg table A config that reflects the structure of neorg.config.user_config.load["module.name"].config +--- @param cfg? table A config that reflects the structure of neorg.config.user_config.load["module.name"].config function modules.load_module_as_dependency(module_name, parent_module, cfg) if modules.load_module(module_name, cfg) and modules.is_module_loaded(parent_module) then modules.loaded_modules[parent_module].required[module_name] = modules.get_module_config(module_name) end end ---- Retrieves the public API exposed by the module ---- @param module_name string The name of the module to retrieve +--- Retrieves the public API exposed by the module. +--- @param module_name string The name of the module to retrieve. +--- @return neorg.module.public? function modules.get_module(module_name) if not modules.is_module_loaded(module_name) then log.trace("Attempt to get module with name", module_name, "failed - module is not loaded.") @@ -524,6 +521,7 @@ end --- Returns the module.config.public table if the module is loaded --- @param module_name string The name of the module to retrieve (module must be loaded) +--- @return table? function modules.get_module_config(module_name) if not modules.is_module_loaded(module_name) then log.trace("Attempt to get module config with name", module_name, "failed - module is not loaded.") @@ -535,13 +533,14 @@ end --- Returns true if module with name module_name is loaded, false otherwise --- @param module_name string The name of an arbitrary module +--- @return boolean function modules.is_module_loaded(module_name) return modules.loaded_modules[module_name] ~= nil end ---- Reads the module's public table and looks for a version variable, then converts it from a string into a table, like so: { major = , minor = , patch = } +--- Reads the module's public table and looks for a version variable, then converts it from a string into a table, like so: `{ major = , minor = , patch = }`. --- @param module_name string The name of a valid, loaded module. --- @Return struct | nil (if any error occurs) +--- @return table? parsed_version function modules.get_module_version(module_name) -- If the module isn't loaded then don't bother retrieving its version if not modules.is_module_loaded(module_name) then @@ -563,7 +562,7 @@ end --- Executes `callback` once `module` is a valid and loaded module, else the callback gets instantly executed. --- @param module_name string The name of the module to listen for. ---- @param callback fun(module_public_table: table) The callback to execute. +--- @param callback fun(module_public_table: neorg.module.public) The callback to execute. function modules.await(module_name, callback) if modules.is_module_loaded(module_name) then callback(assert(modules.get_module(module_name))) @@ -573,7 +572,7 @@ function modules.await(module_name, callback) callbacks.on_event("core.module_loaded", function(_, module) callback(module.public) end, function(event) - return event.content.name == module_name ---@diagnostic disable-line -- TODO: type error workaround + return event.content.name == module_name end) end From 1a7cfb37956e8ee85f91aef0dbc1981734246895 Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Sat, 6 Jan 2024 16:52:06 +0100 Subject: [PATCH 11/15] docs(neorg/core/modules): finish LuaCATS annotations --- lua/neorg/core/modules.lua | 102 +++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 21 deletions(-) diff --git a/lua/neorg/core/modules.lua b/lua/neorg/core/modules.lua index 49a2cb3c1..486438af9 100644 --- a/lua/neorg/core/modules.lua +++ b/lua/neorg/core/modules.lua @@ -576,6 +576,61 @@ function modules.await(module_name, callback) end) end +--- @alias Mode +--- | "n" +--- | "no" +--- | "nov" +--- | "noV" +--- | "noCTRL-V" +--- | "CTRL-V" +--- | "niI" +--- | "niR" +--- | "niV" +--- | "nt" +--- | "Terminal" +--- | "ntT" +--- | "v" +--- | "vs" +--- | "V" +--- | "Vs" +--- | "CTRL-V" +--- | "CTRL-Vs" +--- | "s" +--- | "S" +--- | "CTRL-S" +--- | "i" +--- | "ic" +--- | "ix" +--- | "R" +--- | "Rc" +--- | "Rx" +--- | "Rv" +--- | "Rvc" +--- | "Rvx" +--- | "c" +--- | "cr" +--- | "cv" +--- | "cvr" +--- | "r" +--- | "rm" +--- | "r?" +--- | "!" +--- | "t" + +--- @class (exact) neorg.event +--- @field type string The type of the event. Exists in the format of `category.name`. +--- @field split_type string[] The event type, just split on every `.` character, e.g. `{ "category", "name" }`. +--- @field content? 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 referrer string The name of the module that triggered the event. +--- @field broadcast boolean Whether the event was broadcast to all modules. `true` is so, `false` if the event was specifically sent to a single recipient. +--- @field cursor_position { [1]: number, [2]: number } The position of the cursor at the moment of broadcasting the event. +--- @field filename string The name of the file that the user was in at the moment of broadcasting the event. +--- @field filehead string The directory the user was in at the moment of broadcasting the event. +--- @field line_content string The content of the line the user was editing at the moment of broadcasting the event. +--- @field buffer number The buffer ID of the buffer the user was in at the moment of broadcasting the event. +--- @field window 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. + -- TODO: What goes below this line until the next notice used to belong to modules -- We need to find a way to make these functions easier to maintain @@ -588,6 +643,7 @@ end --- The working of this function is best illustrated with an example: -- If type == 'core.some_plugin.events.my_event', this function will return { 'core.some_plugin', 'my_event' } --- @param type string The full path of a module event +--- @return string[]? function modules.split_event_type(type) local start_str, end_str = type:find("%.events%.") @@ -601,9 +657,10 @@ function modules.split_event_type(type) return split_event_type end ---- Returns an event template defined in module.events.defined ---- @param module table A reference to the module invoking the function ---- @param type string A full path to a valid event type (e.g. 'core.module.events.some_event') +--- Returns an event template defined in `module.events.defined`. +--- @param module neorg.module A reference to the module invoking the function +--- @param type string A full path to a valid event type (e.g. `core.module.events.some_event`) +--- @return neorg.event? function modules.get_event_template(module, type) -- You can't get the event template of a type if the type isn't loaded if not modules.is_module_loaded(module.name) then @@ -625,9 +682,10 @@ function modules.get_event_template(module, type) return modules.loaded_modules[module.name].events.defined[split_type[2]] end ---- Creates a deep copy of the modules.base_event event and returns it with a custom type and referrer ---- @param module table A reference to the module invoking the function ---- @param name string A relative path to a valid event template +--- Creates a deep copy of the `modules.base_event` event and returns it with a custom type and referrer. +--- @param module neorg.module A reference to the module invoking the function. +--- @param name string A relative path to a valid event template. +--- @return neorg.event function modules.define_event(module, name) -- Create a copy of the base event and override the values with ones specified by the user @@ -656,12 +714,12 @@ function modules.define_event(module, name) return new_event end ---- Returns a copy of the event template provided by a module ---- @param module table A reference to the module invoking the function ---- @param type string A full path to a valid event type (e.g. 'core.module.events.some_event') ---- @param content any The content of the event, can be anything from a string to a table to whatever you please ---- @param ev? table the original event data ---- @return table #New event +--- Returns a copy of the event template provided by a module. +--- @param module neorg.module A reference to the module invoking the function +--- @param type string A full path to a valid event type (e.g. `core.module.events.some_event`) +--- @param content table|any? The content of the event, can be anything from a string to a table to whatever you please. +--- @param ev? table The original event data. +--- @return neorg.event? # New event. function modules.create_event(module, type, content, ev) -- Get the module that contains the event local module_name = modules.split_event_type(type)[1] @@ -671,7 +729,7 @@ function modules.create_event(module, type, content, ev) if not event_template then log.warn("Unable to create event of type", type, ". Returning nil...") - return ---@diagnostic disable-line -- TODO: type error workaround + return end -- Make a deep copy here - we don't want to override the actual base table! @@ -682,12 +740,14 @@ function modules.create_event(module, type, content, ev) new_event.referrer = module.name -- Override all the important values - new_event.split_type = modules.split_event_type(type) - new_event.filename = vim.fn.expand("%:t") - new_event.filehead = vim.fn.expand("%:p:h") + new_event.split_type = assert(modules.split_event_type(type)) + new_event.filename = vim.fn.expand("%:t") --[[@as string]] + new_event.filehead = vim.fn.expand("%:p:h") --[[@as string]] + local bufid = ev and ev.buf or vim.api.nvim_get_current_buf() - local winid = vim.fn.bufwinid(bufid) + local winid = assert(vim.fn.bufwinid(bufid)) new_event.cursor_position = vim.api.nvim_win_get_cursor(winid) + local row_1b = new_event.cursor_position[1] new_event.line_content = vim.api.nvim_buf_get_lines(bufid, row_1b - 1, row_1b, true)[1] new_event.referrer = module.name @@ -700,7 +760,7 @@ function modules.create_event(module, type, content, ev) end --- Sends an event to all subscribed modules. The event contains the filename, filehead, cursor position and line content as a bonus. ---- @param event table An event, usually created by modules.create_event() +--- @param event neorg.event An event, usually created by `modules.create_event()`. --- @param callback function? A callback to be invoked after all events have been asynchronously broadcast function modules.broadcast_event(event, callback) -- Broadcast the event to all modules @@ -733,9 +793,9 @@ function modules.broadcast_event(event, callback) end end ---- Instead of broadcasting to all loaded modules, send_event() only sends to one module ---- @param recipient string The name of a loaded module that will be the recipient of the event ---- @param event table An event, usually created by modules.create_event() +--- Instead of broadcasting to all loaded modules, `send_event()` only sends to one module. +--- @param recipient string The name of a loaded module that will be the recipient of the event. +--- @param event neorg.event An event, usually created by `modules.create_event()`. function modules.send_event(recipient, event) -- If the recipient is not loaded then there's no reason to send an event to it if not modules.is_module_loaded(recipient) then From 277aae1ab3a920a2bae5cb6d8042904271138ea1 Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Sat, 6 Jan 2024 16:55:30 +0100 Subject: [PATCH 12/15] docs(neorg/core/callbacks): add LuaCATS annotations --- lua/neorg/core/callbacks.lua | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lua/neorg/core/callbacks.lua b/lua/neorg/core/callbacks.lua index 23123d8a7..503b62f9f 100644 --- a/lua/neorg/core/callbacks.lua +++ b/lua/neorg/core/callbacks.lua @@ -1,16 +1,17 @@ ---[[ - Neorg User Callbacks File - User callbacks are ways for the user to directly interact with Neorg and respond on certain events. ---]] +--- @brief [[ +--- Defines user callbacks - ways for the user to directly interact with Neorg and respon on certain events. +--- @brief ]] +--- @class neorg.callbacks local callbacks = { + ---@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, content) #The function to call whenever our event gets triggered ----@param content_filter fun(event) #A filtering function to test if a certain event meets our expectations +--- 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: neorg.event, content: table|any) The function to call whenever our event gets triggered. +--- @param content_filter fun(event: neorg.event): boolean # A filtering function to test if a certain event meets our expectations. function callbacks.on_event(event_name, callback, content_filter) -- If the table doesn't exist then create it callbacks.callback_list[event_name] = callbacks.callback_list[event_name] or {} @@ -18,8 +19,9 @@ function callbacks.on_event(event_name, callback, content_filter) table.insert(callbacks.callback_list[event_name], { callback, content_filter }) end ---- Used internally by Neorg to call all callbacks with an event ----@param event table #An event as returned by modules.create_event() +--- Used internally by Neorg to call all callbacks with an event. +--- @param event neorg.event An event as returned by `modules.create_event()` +--- @see modules.create_event function callbacks.handle_callbacks(event) -- Query the list of registered callbacks local callback_entry = callbacks.callback_list[event.type] From 1b2040e11a737ab4924bad785eb7e7b0c8cb86e3 Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Sat, 6 Jan 2024 17:07:45 +0100 Subject: [PATCH 13/15] docs(neorg): fix emmylua warning --- lua/neorg.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lua/neorg.lua b/lua/neorg.lua index ae184ac0d..616afc2ca 100644 --- a/lua/neorg.lua +++ b/lua/neorg.lua @@ -6,8 +6,9 @@ local neorg = require("neorg.core") local config, log, modules = neorg.config, neorg.log, neorg.modules --- Initializes Neorg. Parses the supplied user configuration, initializes all selected modules and adds filetype checking for `.norg`. ---- @param cfg table A table that reflects the structure of `config.user_config`. +--- @param cfg neorg.configuration.user A table that reflects the structure of `config.user_config`. --- @see config.user_config +--- @see neorg.configuration.user function neorg.setup(cfg) config.user_config = vim.tbl_deep_extend("force", config.user_config, cfg or {}) @@ -113,6 +114,9 @@ function neorg.org_file_entered(manual, arguments) referrer = "core", line_content = "", broadcast = true, + buffer = vim.api.nvim_get_current_buf(), + window = vim.api.nvim_get_current_win(), + mode = vim.fn.mode(), }) -- Sometimes external plugins prefer hooking in to an autocommand From 93c28b2e54e5ac580e50dac7fbaca3ebe79c86e5 Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Sat, 6 Jan 2024 17:11:49 +0100 Subject: [PATCH 14/15] docs(neorg): import configuration types within the LSP --- lua/neorg.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lua/neorg.lua b/lua/neorg.lua index 616afc2ca..7b3cd4037 100644 --- a/lua/neorg.lua +++ b/lua/neorg.lua @@ -5,6 +5,8 @@ local neorg = require("neorg.core") local config, log, modules = neorg.config, neorg.log, neorg.modules +--- @module "neorg.core.config" + --- Initializes Neorg. Parses the supplied user configuration, initializes all selected modules and adds filetype checking for `.norg`. --- @param cfg neorg.configuration.user A table that reflects the structure of `config.user_config`. --- @see config.user_config From 00f5af858d3050802efc3aa69a0205ef6862e7ab Mon Sep 17 00:00:00 2001 From: Vhyrro Date: Sat, 6 Jan 2024 17:12:57 +0100 Subject: [PATCH 15/15] docs(neorg/core/callbacks): import `modules` module for proper type annotations --- lua/neorg/core/callbacks.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lua/neorg/core/callbacks.lua b/lua/neorg/core/callbacks.lua index 503b62f9f..352a53cf0 100644 --- a/lua/neorg/core/callbacks.lua +++ b/lua/neorg/core/callbacks.lua @@ -2,6 +2,8 @@ --- Defines user callbacks - ways for the user to directly interact with Neorg and respon on certain events. --- @brief ]] +--- @module "neorg.core.modules" + --- @class neorg.callbacks local callbacks = { ---@type table