diff --git a/lua/neorg/core/init.lua b/lua/neorg/core/init.lua index 6d468354c..48a338b57 100644 --- a/lua/neorg/core/init.lua +++ b/lua/neorg/core/init.lua @@ -4,6 +4,9 @@ local neorg = { log = require("neorg.core.log"), modules = require("neorg.core.modules"), utils = require("neorg.core.utils"), + + ---@module "lua-utils" + lib = nil, } return neorg diff --git a/lua/neorg/modules/core/esupports/hop/module.lua b/lua/neorg/modules/core/esupports/hop/module.lua index 0b229c3c5..09a178d5b 100644 --- a/lua/neorg/modules/core/esupports/hop/module.lua +++ b/lua/neorg/modules/core/esupports/hop/module.lua @@ -62,12 +62,53 @@ local function range_contains(r_out, r_in) and xy_le(r_in.row_end, r_in.column_end, r_out.row_end, r_out.column_end) end +---@alias LinkType +---|"url" +---|"generic" +---|"external_file" +---|"definition" +---|"timestamp" +---|"footnote" +---|"heading1" +---|"heading2" +---|"heading3" +---|"heading4" +---|"heading5" +---|"heading6" +---|"line_number" +---|"wiki" + +---@class (exact) Link +---@field link_node TSNode The treesitter node of the link. +---@field link_file_text string? A provided path, if any. +---@field link_type LinkType? The type of link that was provided. +---@field link_location_text string? The target title/URL of the link. +---@field link_description string? The description of the link, if provided. + +---@alias LinkTargetType +--- |"buffer" +--- |"external_app" +--- |"external_file" +--- |"wiki" +--- |"calendar" + +---@class LinkTarget +---@field original_title string The title of the link that points to this target. +---@field node TSNode The node of the target. +---@field type LinkTargetType The type of target that was located. +---@field buffer number The buffer ID in which the target was found. + +---@class (exact) PotentialLinkFixes +---@field similarity number The similarity of this candidate to the current title of the link. +---@field text string The title of the candidate link title (will replace the existing link target). +---@field node TSNode The node the fixed link points to. + ---@class core.esupports.hop module.public = { --- Follow link from a specific node - ---@param node table - ---@param open_mode string|nil if not nil, will open a new split with the split mode defined (vsplitr...) or new tab (mode="tab") or with external app (mode="external") - ---@param parsed_link table a table of link information gathered from parse_link() + ---@param node TSNode + ---@param open_mode string? if not nil, will open a new split with the split mode defined (vsplitr...) or new tab (mode="tab") or with external app (mode="external") + ---@param parsed_link Link A table of link information gathered from parse_link() follow_link = function(node, open_mode, parsed_link) if node:type() == "anchor_declaration" then local located_anchor_declaration = module.public.locate_anchor_declaration_target(node) @@ -85,7 +126,7 @@ module.public = { end if not parsed_link then - log.warn("Please parse your link before calling this function") + log.warn("Please parse your link before calling this function.") return end @@ -281,7 +322,7 @@ module.public = { end, --- Locate a `link` or `anchor` node under the cursor - ---@return userdata|nil #A `link` or `anchor` node if present under the cursor, else `nil` + ---@return TSNode? #A `link` or `anchor` node if present under the cursor, else `nil` extract_link_node = function() local ts_utils = module.required["core.integrations.treesitter"].get_ts_utils() @@ -303,7 +344,7 @@ module.public = { end, --- Attempts to locate a `link` or `anchor` node after the cursor on the same line - ---@return userdata|nil #A `link` or `anchor` node if present on the current line, else `nil` + ---@return TSNode? #A `link` or `anchor` node if present on the current line, else `nil` lookahead_link_node = function() local ts_utils = module.required["core.integrations.treesitter"].get_ts_utils() @@ -346,7 +387,8 @@ module.public = { end, --- Locates the node that an anchor is pointing to - ---@param anchor_decl_node table #A valid anchod declaration node + ---@param anchor_decl_node TSNode #A valid anchor declaration node + ---@return LinkTarget? #The target of the link if it was found. locate_anchor_declaration_target = function(anchor_decl_node) if not anchor_decl_node:named_child(0) then return @@ -355,7 +397,7 @@ module.public = { local target = module .required ["core.integrations.treesitter"] - .get_node_text(anchor_decl_node:named_child(0):named_child(0)) ---@diagnostic disable-line -- TODO: type error workaround + .get_node_text(anchor_decl_node:named_child(0):named_child(0)) :gsub("[%s\\]", "") local query_str = [[ @@ -392,12 +434,13 @@ module.public = { end, --- Converts a link node into a table of data related to the link - ---@param link_node userdata #The link node that was found by e.g. `extract_link_node()` + ---@param link_node TSNode #The link node that was found by e.g. `extract_link_node()` ---@param buf number #The buffer to parse the link in - ---@return table? #A table of data about the link + ---@return Link? #A table of data about the link parse_link = function(link_node, buf) buf = buf or 0 - if not link_node or not vim.tbl_contains({ "link", "anchor_definition" }, link_node:type()) then ---@diagnostic disable-line -- TODO: type error workaround + + if not link_node or not vim.tbl_contains({ "link", "anchor_definition" }, link_node:type()) then return end @@ -459,15 +502,18 @@ module.public = { ] ]] + ---@type TSNode? local document_root = module.required["core.integrations.treesitter"].get_document_root(buf) if not document_root then return end + ---@type vim.treesitter.Query local query = utils.ts_parse_query("norg", query_text) local range = module.required["core.integrations.treesitter"].get_node_range(link_node) + ---@type Link local parsed_link_information = { link_node = link_node, } @@ -500,8 +546,8 @@ module.public = { end, --- Locate the target that a link points to - ---@param parsed_link_information table #A table returned by `parse_link()` - ---@return table #A table containing data about the link target + ---@param parsed_link_information Link #A table returned by `parse_link()` + ---@return LinkTarget #A table containing data about the link target locate_link_target = function(parsed_link_information) --- A pointer to the target buffer we will be parsing. -- This may change depending on the target file the user gave. @@ -537,7 +583,9 @@ module.public = { end, external_file = function() - local destination = parsed_link_information.link_location_text + -- There has to be a link location present for a link to be recognized as an external file, + -- therefore we can safely assert here. + local destination = assert(parsed_link_information.link_location_text) local path, line = string.match(destination, "^(.*):(%d+)$") if line then destination = path @@ -550,11 +598,11 @@ module.public = { return lib.match(vim.fn.fnamemodify(destination, ":e"))({ [{ "jpg", "jpeg", "png", "pdf" }] = { type = "external_app", - uri = vim.uri_from_fname(vim.fn.expand(destination)), ---@diagnostic disable-line -- TODO: type error workaround + uri = vim.uri_from_fname(vim.fn.expand(destination)), }, [module.config.public.external_filetypes] = { type = "external_app", - uri = vim.uri_from_fname(vim.fn.expand(destination)), ---@diagnostic disable-line -- TODO: type error workaround + uri = vim.uri_from_fname(vim.fn.expand(destination)), }, _ = function() return { @@ -629,7 +677,7 @@ module.public = { end end end, - }) + } --[[@as table]]) end, } @@ -692,8 +740,8 @@ module.private = { end, --- Fuzzy fixes a link with a loose type checking query - ---@param parsed_link_information table #A table as returned by `parse_link()` - ---@return table #A table of similarities (fuzzed items) + ---@param parsed_link_information Link #A table as returned by `parse_link()` + ---@return PotentialLinkFixes[]? #A table of similarities (fuzzed items) fix_link_loose = function(parsed_link_information) local generic_query = [[ [(_ @@ -716,8 +764,8 @@ module.private = { end, --- Fuzzy fixes a link with a strict type checking query - ---@param parsed_link_information table #A table as returned by `parse_link()` - ---@return table #A table of similarities (fuzzed items) + ---@param parsed_link_information Link #A table as returned by `parse_link()` + ---@return PotentialLinkFixes[]? #A table of similarities (fuzzed items) fix_link_strict = function(parsed_link_information) local query = lib.match(parsed_link_information.link_type)({ generic = [[ @@ -790,7 +838,7 @@ module.private = { --- Query all similar targets that a link could be pointing to ---@param parsed_link_information table #A table as returned by `parse_link()` ---@param query_str string #The query to be used during the search - ---@return table #A table of similarities (fuzzed items) + ---@return PotentialLinkFixes[]? #A table of similarities (fuzzed items) fix_link = function(parsed_link_information, query_str) local buffer = vim.api.nvim_get_current_buf() @@ -808,12 +856,11 @@ module.private = { local document_root = module.required["core.integrations.treesitter"].get_document_root(buffer) if not document_root then - return ---@diagnostic disable-line -- TODO: type error workaround + return end - local similarities = { - -- Example: { 0.6, "title", node } - } + ---@type PotentialLinkFixes[] + local similarities = {} for id, node in query:iter_captures(document_root, buffer) do local capture_name = query.captures[id] @@ -841,9 +888,9 @@ module.private = { end, --- Writes a link that was fixed through fuzzing into the buffer - ---@param link_node userdata #The treesitter node of the link, extracted by e.g. `extract_link_node()` - ---@param parsed_link_information table #A table as returned by `parse_link()` - ---@param similarities table #The table of similarities as returned by `fix_link_*()` + ---@param link_node TSNode #The treesitter node of the link, extracted by e.g. `extract_link_node()` + ---@param parsed_link_information Link #A table as returned by `parse_link()` + ---@param similarities PotentialLinkFixes[] #The table of similarities as returned by `fix_link_*()` ---@param force_type boolean #If true will forcefully overwrite the link type to the target type as well (e.g. would convert `#` -> `*`) write_fixed_link = function(link_node, parsed_link_information, similarities, force_type) local most_similar = similarities[1] @@ -882,11 +929,10 @@ module.private = { { replace } ) end - callback( "{" .. lib.when( - parsed_link_information.link_file_text, + parsed_link_information.link_file_text --[[@as boolean]], lib.lazy_string_concat(":", parsed_link_information.link_file_text, ":"), "" ) @@ -894,7 +940,7 @@ module.private = { .. most_similar.text .. "}" .. lib.when( - parsed_link_information.link_description, + parsed_link_information.link_description --[[@as boolean]], lib.lazy_string_concat("[", parsed_link_information.link_description, "]"), "" ) @@ -906,7 +952,6 @@ module.on_event = function(event) if event.split_type[2] == "core.esupports.hop.hop-link" then local split_mode = event.content[1] - -- Get link node at cursor local link_node_at_cursor = module.public.extract_link_node() if not link_node_at_cursor then @@ -914,9 +959,9 @@ module.on_event = function(event) return end - local parsed_link = module.public.parse_link(link_node_at_cursor) ---@diagnostic disable-line -- TODO: type error workaround + local parsed_link = module.public.parse_link(link_node_at_cursor) - module.public.follow_link(link_node_at_cursor, split_mode, parsed_link) ---@diagnostic disable-line -- TODO: type error workaround + module.public.follow_link(link_node_at_cursor, split_mode, parsed_link) end end