diff --git a/.styluaignore b/.styluaignore index 06c52ba0e6..f4dbff71e9 100644 --- a/.styluaignore +++ b/.styluaignore @@ -1,9 +1 @@ -.github -after -docs -ftdetect -res - -# There is a bug in stylua where it cant parse statements with `goto`, so this file is ignored until its resolved -# https://github.com/JohnnyMorganz/StyLua/issues/229 -lua/neorg/modules/core/norg/completion/module.lua +ldoc/ diff --git a/lua/neorg/modules/core/keybinds/module.lua b/lua/neorg/modules/core/keybinds/module.lua index de22b8ad30..8980353147 100644 --- a/lua/neorg/modules/core/keybinds/module.lua +++ b/lua/neorg/modules/core/keybinds/module.lua @@ -119,10 +119,24 @@ module.public = { -- @Param command (string) - same as the rhs parameter for :h nvim_buf_set_keymap -- @Param opts (table) - same as the opts parameter for :h nvim_buf_set_keymap map = function(mode, key, command, opts) - local ok, error = pcall(function() if action then action(0, mode, key, command, opts or {}) else vim.api.nvim_buf_set_keymap(0, mode, key, command, opts or {}) end end) + local ok, error = pcall(function() + if action then + action(0, mode, key, command, opts or {}) + else + vim.api.nvim_buf_set_keymap(0, mode, key, command, opts or {}) + end + end) if not ok then - log.trace(string.format("An error occurred when trying to bind key '%s' in mode '%s' in neorg mode '%s' - %s", key, mode, current_mode, error)) + log.trace( + string.format( + "An error occurred when trying to bind key '%s' in mode '%s' in neorg mode '%s' - %s", + key, + mode, + current_mode, + error + ) + ) end end, @@ -283,11 +297,15 @@ module.on_event = function(event) module.public.sync() elseif event.type == "core.mode.events.mode_set" then -- If a new mode has been set then reset all of our keybinds - module.public.bind_all(function(buf, mode, key) vim.api.nvim_buf_del_keymap(buf, mode, key) end, event.content.current) + module.public.bind_all(function(buf, mode, key) + vim.api.nvim_buf_del_keymap(buf, mode, key) + end, event.content.current) module.public.bind_all() elseif event.type == "core.autocommands.events.bufenter" and event.content.norg then -- If a new mode has been set then reset all of our keybinds - module.public.bind_all(function(buf, mode, key) vim.api.nvim_buf_del_keymap(buf, mode, key) end, module.required["core.mode"].get_previous_mode()) + module.public.bind_all(function(buf, mode, key) + vim.api.nvim_buf_del_keymap(buf, mode, key) + end, module.required["core.mode"].get_previous_mode()) module.public.bind_all() end end diff --git a/lua/neorg/modules/core/mode/module.lua b/lua/neorg/modules/core/mode/module.lua index 59de69dc8b..6e03c7ea2e 100644 --- a/lua/neorg/modules/core/mode/module.lua +++ b/lua/neorg/modules/core/mode/module.lua @@ -31,7 +31,6 @@ local module = neorg.modules.create("core.mode") local log = require("neorg.external.log") module.config.public = { - -- As the name suggests, stores the current and previous mode current_mode = "norg", previous_mode = "norg", diff --git a/lua/neorg/modules/core/norg/completion/module.lua b/lua/neorg/modules/core/norg/completion/module.lua index 1d86afba24..93268a2623 100644 --- a/lua/neorg/modules/core/norg/completion/module.lua +++ b/lua/neorg/modules/core/norg/completion/module.lua @@ -3,8 +3,8 @@ A wrapper to interface with several different completion engines. Is currently in its beta phase, doesn't work in all situations. --]] -require('neorg.modules.base') -require('neorg.modules') +require("neorg.modules.base") +require("neorg.modules") local module = neorg.modules.create("core.norg.completion") @@ -15,11 +15,11 @@ module.config.public = { } module.setup = function() - return { success = true, requires = { "core.integrations.treesitter" } } + return { success = true, requires = { "core.integrations.treesitter" } } end module.private = { - engine = nil + engine = nil, } module.load = function() @@ -46,7 +46,7 @@ module.load = function() -- Create the integration engine's source module.private.engine.create_source({ - completions = module.config.public.completions + completions = module.config.public.completions, }) end @@ -55,331 +55,337 @@ module.public = { -- Define completions completions = { { -- Create a new completion - -- Define the regex that should match in order to proceed - regex = "^%s*@(%w*)", + -- Define the regex that should match in order to proceed + regex = "^%s*@(%w*)", + + -- If regex can be matched, this item then gets verified via TreeSitter's AST + node = function(current, previous) + -- If no previous node exists then try verifying the current node instead + if not previous then + return current and (current:type() ~= "translation_unit" or current:type() == "document") or false + end - -- If regex can be matched, this item then gets verified via TreeSitter's AST - node = function(current, previous) + -- If the previous node is not tag parameters or the tag name + -- (i.e. we are not inside of a tag) then show autocompletions + return previous:type() ~= "tag_parameters" and previous:type() ~= "tag_name" + end, - -- If no previous node exists then try verifying the current node instead - if not previous then - return current and (current:type() ~= "translation_unit" or current:type() == "document") or false - end + -- The actual elements to show if the above tests were true + complete = { + "table", + "code", + "image", + "embed", + "name", + "document", + }, - -- If the previous node is not tag parameters or the tag name - -- (i.e. we are not inside of a tag) then show autocompletions - return previous:type() ~= "tag_parameters" and previous:type() ~= "tag_name" - end, - - -- The actual elements to show if the above tests were true - complete = { - "table", - "code", - "image", - "embed", - "name", - "document", - }, + -- Additional options to pass to the completion engine + options = { + type = "Tag", + completion_start = "@", + }, - -- Additional options to pass to the completion engine - options = { - type = "Tag", - completion_start = "@", - }, + -- We might have matched the top level item, but can we match it with any + -- more precision? Descend down the rabbit hole and try to more accurately match + -- the line. + descend = { + -- The cycle continues + { + regex = "document%.%w*", - -- We might have matched the top level item, but can we match it with any - -- more precision? Descend down the rabbit hole and try to more accurately match - -- the line. - descend = { - -- The cycle continues - { - regex = "document%.%w*", + complete = { + "meta", + }, - complete = { - "meta", - }, + options = { + type = "Tag", + }, - options = { - type = "Tag", + descend = {}, }, + { + -- Define a regex (gets appended to parent's regex) + regex = "code%s+%w*", + -- No node variable, we don't need that sort of check here - descend = {}, - }, - { - -- Define a regex (gets appended to parent's regex) - regex = "code%s+%w*", - -- No node variable, we don't need that sort of check here + complete = require("neorg.external.helpers").get_language_list(true), - complete = require('neorg.external.helpers').get_language_list(true), + -- Extra options + options = { + type = "Language", + }, - -- Extra options - options = { - type = "Language" + -- Don't descend any further, we've narrowed down our match + descend = {}, }, + { + regex = "tangle%s+%w*", - -- Don't descend any further, we've narrowed down our match - descend = {} - }, - { - regex = "tangle%s+%w*", - - complete = { - "" - }, + complete = { + "", + }, - options = { - type = "Property" - } - }, - { - regex = "image%s+%w*", - - complete = { - "jpeg", - "png", - "svg", - "jfif", - "exif", + options = { + type = "Property", + }, }, - - options = { - type = "Format" + { + regex = "image%s+%w*", + + complete = { + "jpeg", + "png", + "svg", + "jfif", + "exif", + }, + + options = { + type = "Format", + }, }, - }, - { - regex = "embed%s+%w*", + { + regex = "embed%s+%w*", - complete = { - "video", - "image", - }, + complete = { + "video", + "image", + }, - options = { - type = "Embed", + options = { + type = "Embed", + }, }, }, - } - }, - { - regex = "^%s*%$(%w*)", - - complete = { - "comment", - "ordered", }, + { + regex = "^%s*%$(%w*)", + + complete = { + "comment", + "ordered", + }, + options = { + type = "Tag", + }, - options = { - type = "Tag", + descend = {}, }, + { + regex = "^%s*@e?n?", + node = function(_, previous) + if not previous then + return false + end - descend = {}, - }, - { - regex = "^%s*@e?n?", - node = function(_, previous) - if not previous then return false end + return previous:type() == "tag_parameters" or previous:type() == "tag_name" + end, - return previous:type() == "tag_parameters" or previous:type() == "tag_name" - end, + complete = { + "end", + }, - complete = { - "end" + options = { + type = "Directive", + completion_start = "@", + }, }, + { + regex = "^%s*%-+%s+%[([x%*%s]?)", - options = { - type = "Directive", - completion_start = "@", - } - }, - { - regex = "^%s*%-+%s+%[([x%*%s]?)", - - complete = { - "[ ] ", - "[*] ", - "[x] ", - }, + complete = { + "[ ] ", + "[*] ", + "[x] ", + }, - options = { - type = "TODO", - pre = function() - local sub = vim.api.nvim_get_current_line():gsub("^(%s*%-+%s+%[%s*)%]", "%1") + options = { + type = "TODO", + pre = function() + local sub = vim.api.nvim_get_current_line():gsub("^(%s*%-+%s+%[%s*)%]", "%1") - if sub then - vim.api.nvim_set_current_line(sub) - end - end, - completion_start = "-" - } + if sub then + vim.api.nvim_set_current_line(sub) + end + end, + completion_start = "-", + }, + }, }, -}, - --- @Summary Provides completions to the integration engine --- @Description Parses the public completion table and attempts to find all valid matches --- @Param context (table) - the context provided by the integration engine --- @Param prev (table) - the previous table of completions - used for descent --- @Param saved (string) - the saved regex in the form of a string, used to concatenate children nodes with parent nodes' regexes -complete = function(context, prev, saved) - -- If the save variable wasn't passed then set it to an empty string - saved = saved or "" - - -- If we haven't defined any explicit table to read then read the public completions table - local completions = prev or module.public.completions - - -- Loop through every completion - for _, completion_data in ipairs(completions) do - -- Construct a variable that will be returned on a successful match - local ret_completions = { items = completion_data.complete, options = completion_data.options or {} } - - -- If the completion data has a regex variable - if completion_data.regex then - -- Attempt to match the current line before the cursor with that regex - local match = context.line:match(saved .. completion_data.regex .. "$") - - -- If our match was successful - if match then - -- Set the match variable for the integration module - ret_completions.match = match - - -- If the completion data has a node variable then attempt to match the current node too! - if completion_data.node then - -- Grab the treesitter utilities - local ts = module.required["core.integrations.treesitter"].get_ts_utils() - - -- If the type of completion data we're dealing with is a string then attempt to parse it - if type(completion_data.node) == "string" then - -- Split the completion node string down every pipe character - local split = vim.split(completion_data.node, "|") - -- Check whether the first character of the string is an exclamation mark - -- If this is present then it means we're looking for a node that *isn't* the one we specify - local negate = split[1]:sub(0, 1) == "!" - - -- If we are negating then remove the leading exclamation mark so it doesn't interfere - if negate then - split[1] = split[1]:sub(2) - end - -- If we have a second split (i.e. in the string "tag_name|prev" this would be the "prev" string) - if split[2] then - -- Is our other value "prev"? If so, compare the current node in the syntax tree with the previous node - if split[2] == "prev" then + -- @Summary Provides completions to the integration engine + -- @Description Parses the public completion table and attempts to find all valid matches + -- @Param context (table) - the context provided by the integration engine + -- @Param prev (table) - the previous table of completions - used for descent + -- @Param saved (string) - the saved regex in the form of a string, used to concatenate children nodes with parent nodes' regexes + complete = function(context, prev, saved) + -- If the save variable wasn't passed then set it to an empty string + saved = saved or "" + + -- If we haven't defined any explicit table to read then read the public completions table + local completions = prev or module.public.completions + + -- Loop through every completion + for _, completion_data in ipairs(completions) do + -- Construct a variable that will be returned on a successful match + local ret_completions = { items = completion_data.complete, options = completion_data.options or {} } + + -- If the completion data has a regex variable + if completion_data.regex then + -- Attempt to match the current line before the cursor with that regex + local match = context.line:match(saved .. completion_data.regex .. "$") + + -- If our match was successful + if match then + -- Set the match variable for the integration module + ret_completions.match = match + + -- If the completion data has a node variable then attempt to match the current node too! + if completion_data.node then + -- Grab the treesitter utilities + local ts = module.required["core.integrations.treesitter"].get_ts_utils() + + -- If the type of completion data we're dealing with is a string then attempt to parse it + if type(completion_data.node) == "string" then + -- Split the completion node string down every pipe character + local split = vim.split(completion_data.node, "|") + -- Check whether the first character of the string is an exclamation mark + -- If this is present then it means we're looking for a node that *isn't* the one we specify + local negate = split[1]:sub(0, 1) == "!" + + -- If we are negating then remove the leading exclamation mark so it doesn't interfere + if negate then + split[1] = split[1]:sub(2) + end - -- Get the previous node - local previous_node = ts.get_previous_node(ts.get_node_at_cursor(), true, true) + -- If we have a second split (i.e. in the string "tag_name|prev" this would be the "prev" string) + if split[2] then + -- Is our other value "prev"? If so, compare the current node in the syntax tree with the previous node + if split[2] == "prev" then + -- Get the previous node + local previous_node = ts.get_previous_node(ts.get_node_at_cursor(), true, true) + + -- If the previous node is nil + if not previous_node then + -- If we have specified a negation then that means our tag type doesn't match the previous tag's type, + -- which is good! That means we can return our completions + if negate then + return ret_completions + end + + -- Otherwise continue on with the loop + goto continue + end - -- If the previous node is nil - if not previous_node then - -- If we have specified a negation then that means our tag type doesn't match the previous tag's type, - -- which is good! That means we can return our completions - if negate then + -- If we haven't negated and the previous node type is equal to the one we specified then return completions + if not negate and previous_node:type() == split[1] then + return ret_completions + -- Otherwise, if we want to negate and if the current node type is not equal to the one we specified + -- then also return completions - it means the match was successful + elseif negate and previous_node:type() ~= split[1] then return ret_completions + else -- Otherwise just continue with the loop + goto continue + end + -- Else if our second split is equal to "next" then it's time to inspect the next node in the AST + elseif split[2] == "next" then + -- Grab the next node + local next_node = ts.get_next_node(ts.get_node_at_cursor(), true, true) + + -- If it's nil + if not next_node then + -- If we want to negate then return completions - the comparison was unsuccessful, which is what we wanted + if negate then + return ret_completions + end + + -- Or just continue + goto continue end - -- Otherwise continue on with the loop - goto continue - end - - -- If we haven't negated and the previous node type is equal to the one we specified then return completions - if not negate and previous_node:type() == split[1] then - return ret_completions - -- Otherwise, if we want to negate and if the current node type is not equal to the one we specified - -- then also return completions - it means the match was successful - elseif negate and previous_node:type() ~= split[1] then - return ret_completions - else -- Otherwise just continue with the loop - goto continue - end - -- Else if our second split is equal to "next" then it's time to inspect the next node in the AST - elseif split[2] == "next" then - - -- Grab the next node - local next_node = ts.get_next_node(ts.get_node_at_cursor(), true, true) - - -- If it's nil - if not next_node then - -- If we want to negate then return completions - the comparison was unsuccessful, which is what we wanted - if negate then + -- If we are not negating and the node values match then return completions + if not negate and next_node:type() == split[1] then return ret_completions + -- If we are negating and then values don't match then also return completions + elseif negate and next_node:type() ~= split[1] then + return ret_completions + else + -- Else keep look through the completion table to see whether we can find another match + goto continue end - - -- Or just continue - goto continue end - - -- If we are not negating and the node values match then return completions - if not negate and next_node:type() == split[1] then - return ret_completions - -- If we are negating and then values don't match then also return completions - elseif negate and next_node:type() ~= split[1] then - return ret_completions - else - -- Else keep look through the completion table to see whether we can find another match - goto continue + else -- If we haven't defined a split (no pipe was found) then compare the current node + if ts.get_node_at_cursor():type() == split[1] then + -- If we're not negating then return completions + if not negate then + return ret_completions + else -- Else continue + goto continue + end end end - else -- If we haven't defined a split (no pipe was found) then compare the current node - if ts.get_node_at_cursor():type() == split[1] then - -- If we're not negating then return completions - if not negate then - return ret_completions - else -- Else continue - goto continue - end + -- If our completion data type is not a string but rather it is a function then + elseif type(completion_data.node) == "function" then + -- Grab all the necessary variables (current node, previous node, next node) + local current_node = ts.get_node_at_cursor() + local next_node = ts.get_next_node(current_node, true, true) + local previous_node = ts.get_previous_node(current_node, true, true) + + -- Execute the callback function with all of our parameters. + -- If it returns true then that means the match was successful, and so return completions + if completion_data.node(current_node, previous_node, next_node, ts) then + return ret_completions end - end - -- If our completion data type is not a string but rather it is a function then - elseif type(completion_data.node) == "function" then - -- Grab all the necessary variables (current node, previous node, next node) - local current_node = ts.get_node_at_cursor() - local next_node = ts.get_next_node(current_node, true, true) - local previous_node = ts.get_previous_node(current_node, true, true) - - -- Execute the callback function with all of our parameters. - -- If it returns true then that means the match was successful, and so return completions - if completion_data.node(current_node, previous_node, next_node, ts) then - return ret_completions - end - -- If no completions were found, try looking whether we can descend any further down the syntax tree. - -- Maybe we can find something extra there? - if completion_data.descend then - -- Recursively call complete() with the nested table - local descent = module.public.complete(context, completion_data.descend, saved .. completion_data.regex) - - -- If the returned completion items actually hold some data (i.e. a match was found) then return those matches - if not vim.tbl_isempty(descent.items) then - return descent + -- If no completions were found, try looking whether we can descend any further down the syntax tree. + -- Maybe we can find something extra there? + if completion_data.descend then + -- Recursively call complete() with the nested table + local descent = module.public.complete( + context, + completion_data.descend, + saved .. completion_data.regex + ) + + -- If the returned completion items actually hold some data (i.e. a match was found) then return those matches + if not vim.tbl_isempty(descent.items) then + return descent + end end - end - -- Else just don't bother and continue - goto continue + -- Else just don't bother and continue + goto continue + end end - end - -- If none of the checks matched, then we can conclude that only the regex variable was defined, - -- and since that was matched properly, we can return all completions. - return ret_completions - -- If the regex for the current line wasn't matched then attempt to descend further down, - -- similarly to what we did earlier - elseif completion_data.descend then - -- Recursively call function with new parameters - local descent = module.public.complete(context, completion_data.descend, saved .. completion_data.regex) - - -- If we had some completions from that function then return those completions - if not vim.tbl_isempty(descent.items) then - return descent + -- If none of the checks matched, then we can conclude that only the regex variable was defined, + -- and since that was matched properly, we can return all completions. + return ret_completions + -- If the regex for the current line wasn't matched then attempt to descend further down, + -- similarly to what we did earlier + elseif completion_data.descend then + -- Recursively call function with new parameters + local descent = module.public.complete( + context, + completion_data.descend, + saved .. completion_data.regex + ) + + -- If we had some completions from that function then return those completions + if not vim.tbl_isempty(descent.items) then + return descent + end end end - end - ::continue:: - end + ::continue:: + end - -- If absolutely no matches were found return empty data (no completions) - return { items = {}, options = {} } -end + -- If absolutely no matches were found return empty data (no completions) + return { items = {}, options = {} } + end, } return module diff --git a/lua/neorg/modules/core/norg/concealer/module.lua b/lua/neorg/modules/core/norg/concealer/module.lua index 4c4038aeeb..c5e7f5a1d5 100644 --- a/lua/neorg/modules/core/norg/concealer/module.lua +++ b/lua/neorg/modules/core/norg/concealer/module.lua @@ -898,6 +898,12 @@ module.on_event = function(event) event.cursor_position[1] - 1, event.cursor_position[1] ) + vim.api.nvim_buf_clear_namespace( + 0, + module.private.completion_level_namespace, + event.cursor_position[1] - 1, + event.cursor_position[1] + ) elseif event.type == "core.autocommands.events.insertleave" then vim.schedule(function() module.public.trigger_icons(event.cursor_position[1])