Skip to content

Commit

Permalink
chore: proper LuaCATS annotations for esupports.hop
Browse files Browse the repository at this point in the history
  • Loading branch information
vhyrro committed Jun 25, 2024
1 parent f33a491 commit 57daf53
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 35 deletions.
3 changes: 3 additions & 0 deletions lua/neorg/core/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
115 changes: 80 additions & 35 deletions lua/neorg/modules/core/esupports/hop/module.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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()

Expand All @@ -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()

Expand Down Expand Up @@ -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
Expand All @@ -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 <pysan3>
.get_node_text(anchor_decl_node:named_child(0):named_child(0))
:gsub("[%s\\]", "")

local query_str = [[
Expand Down Expand Up @@ -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 <pysan3>

if not link_node or not vim.tbl_contains({ "link", "anchor_definition" }, link_node:type()) then
return
end

Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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 <pysan3>
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 <pysan3>
uri = vim.uri_from_fname(vim.fn.expand(destination)),
},
_ = function()
return {
Expand Down Expand Up @@ -629,7 +677,7 @@ module.public = {
end
end
end,
})
} --[[@as table<string, fun(): LinkTarget?>]])
end,
}

Expand Down Expand Up @@ -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 = [[
[(_
Expand All @@ -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 = [[
Expand Down Expand Up @@ -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()

Expand All @@ -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 <pysan3>
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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -882,19 +929,18 @@ 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, ":"),
""
)
.. prefix
.. 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, "]"),
""
)
Expand All @@ -906,17 +952,16 @@ 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
log.trace("No link under cursor.")
return
end

local parsed_link = module.public.parse_link(link_node_at_cursor) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>
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 <pysan3>
module.public.follow_link(link_node_at_cursor, split_mode, parsed_link)
end
end

Expand Down

0 comments on commit 57daf53

Please sign in to comment.