diff --git a/.github/workflows/gendocs.yml b/.github/workflows/gendocs.yml new file mode 100644 index 0000000..584b549 --- /dev/null +++ b/.github/workflows/gendocs.yml @@ -0,0 +1,25 @@ +name: Generate Documentation + +on: + workflow_dispatch: + +jobs: + generate-docs: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Neovim + run: | + sudo apt-get update + sudo apt-get install -y neovim + + - name: Install Lua and mini.doc Dependencies + run: | + sudo apt-get install -y lua5.3 + mkdir -p ~/.config/nvim/pack/plugins/start + git clone https://github.com/echasnovski/mini.nvim.git ~/.config/nvim/pack/plugins/start/mini.nvim + + - name: Run Documentation Generation + run: make gendocs diff --git a/ci/Makefile b/ci/Makefile index afafe63..cbee89d 100644 --- a/ci/Makefile +++ b/ci/Makefile @@ -77,3 +77,7 @@ lua_ls-debug: else \ echo "✅ No errors found."; \ fi + +.PHONY: gendocs + gendocs: + sh ./scripts/gendoc.sh diff --git a/lua/ninjection.lua b/lua/ninjection.lua index 09fa0ab..1006333 100644 --- a/lua/ninjection.lua +++ b/lua/ninjection.lua @@ -1,24 +1,14 @@ ---@module "ninjection" ----@tag ninjection ----@brief [[ ---- Ninjection is a plugin designed to improve editing injected language text. ---- Its goal is to provide a seamless, first-class editing experience for injected ---- code with full support for LSPs, code-snippets, completions, formatting, etc. ---- ---- Ninjection utilizes Treesitter's language parsing functionality to identify ---- nodes that contain an injected language, and appropriately designate the ---- that language. It provides functions to create a new buffer for that language, ---- and to attach an appropriate LSP to that buffer. ---- ---- While Ninjection was written primarily to edit injected languages in Nix files, ---- it should be easily extensible to other languages. Ninjection provides ---- configuration options to modify language parsing queries, LSP mappings, ---- window styles, and formatting. ----]] -local M = {} +---@brief +--- The ninjection module contains the three primary ninjection functions: +--- |select()|, |edit()|, and |replace()|. + +local ninjection = {} +---@nodoc ---@type Ninjection.Config local cfg = require("ninjection.config").cfg + local ts = require("vim.treesitter") local util = require("ninjection.util") local nts = require("ninjection.treesitter") @@ -27,12 +17,14 @@ if vim.fn.exists(":checkhealth") == 2 then require("ninjection.health").check() end ---- Function: Identify and select injected content text in visual mode. ----@return nil|string err Error string, if applicable. -M.select = function() - -- TODO: Remove any, use unkown and refine type - -- TODO: Style - replace |nil with ? - ---@type boolean, unknown?, string?, integer?, NJNodeTable? +---@tag ninjection.select() +---@brief +--- Identifies and selects injected text in visual mode. +--- +---@return string? err +--- +ninjection.select = function() + ---@type boolean, unknown, string?, integer?, NJNodeTable? local ok, raw_output, err, bufnr, node_info ok, raw_output = pcall(function() @@ -51,7 +43,7 @@ M.select = function() return nil end bufnr = raw_output - ---@cast bufnr number + ---@cast bufnr integer node_info, err = nts.get_node_table(cfg.inj_lang_query, cfg.file_lang) if not node_info then @@ -102,15 +94,18 @@ M.select = function() return nil end ---- Function: Detect injected languages at the cursor position and begin ---- editing supported languages according to configured preferences. ---- Creates a child buffer with an NJChild object that stores config information ---- for itself and information to replace text in the parent buffer. It also ---- appends the child buffer handle to an NJParent object in the parent buffer. ----@return nil|string err Error string, if applicable. -M.edit = function() - -- Configuration is loaded in the function vs. module to allow for dynamic changes. - ---@type boolean, unknown?, string?, integer?, string?, string? +---@tag ninjection.edit() +---@brief +--- Detects injected languages at the cursor position and begins editing supported +--- languages according to configured preferences. `ninjection.edit()` creates a +--- child buffer with an `NJChild` object that stores config information for itself +--- and information to replace text in the parent buffer. It also appends the child +--- buffer handle to an `NJParent` object in the parent buffer. +--- +---@return string? err +--- +ninjection.edit = function() + ---@type boolean, unknown, string?, integer?, string?, string? local ok, raw_output, err, p_bufnr, inj_node_text, inj_node_lang ok, raw_output = pcall(function() @@ -119,10 +114,10 @@ M.edit = function() if not ok then error(tostring(raw_output), 2) end - p_bufnr = raw_output - if not p_bufnr then + if type(raw_output) ~= "number" then error("ninjection.edit() error: Could not retrieve current buffer handle.", 2) end + p_bufnr = raw_output ---@cast p_bufnr integer ---@type NJNodeTable? @@ -184,15 +179,18 @@ M.edit = function() if not ok then error(tostring(raw_output), 2) end + ---@type integer[]? - local p_cursor = raw_output - if not p_cursor then - if not cfg.suppress_warnings then - vim.notify( - "ninjection.edit() warning: No cursor position returned from " .. "vim.api.nvim_win_get_cursor(0)", - vim.log.levels.WARN - ) - end + local p_cursor + if type(raw_output) == "table" then + p_cursor = raw_output + ---@cast p_cursor integer[] + elseif not cfg.suppress_warnings then + vim.notify( + "ninjection.edit() warning: No cursor position returned from " .. "vim.api.nvim_win_get_cursor(0)", + vim.log.levels.WARN + ) + p_cursor = {} -- Don't return on failed cursor end ---@cast p_cursor integer[] @@ -203,9 +201,7 @@ M.edit = function() if not ok then error(tostring(raw_output), 2) end - ---@type string? - local p_name = raw_output - if not p_name or p_name == "" then + if type(raw_output) ~= "string" then if not cfg.suppress_warnings then vim.notify( "ninjection.edit() warning: No name returned from " .. "vim.api.nvim_buf_get_name(0)", @@ -214,22 +210,23 @@ M.edit = function() end return nil end - ---@cast p_name string + ---@type string + local p_name = raw_output - ---@type string? + ---@type string local root_dir -- Try getting the first workspace folder. ok, raw_output = pcall(function() return vim.lsp.buf.list_workspace_folders()[1] end) - if ok and raw_output and raw_output ~= "" then + if ok and type(raw_output) == "string" and raw_output ~= "" then root_dir = raw_output else -- Fall back to the current working directory. local nested_ok, nested_raw_output = pcall(function() return vim.fn.getcwd() end) - if nested_ok and nested_raw_output and nested_raw_output ~= "" then + if nested_ok and type(nested_raw_output) == "string" and nested_raw_output ~= "" then root_dir = nested_raw_output else error( @@ -245,7 +242,6 @@ M.edit = function() if not root_dir or root_dir == "" then error("ninjection.edit() error: Unknown error setting root_dir", 2) end - ---@cast root_dir string ---@type {bufnr: integer?, win: integer?, indents: NJIndents} local c_table @@ -257,7 +253,7 @@ M.edit = function() if cfg.preserve_indents then util.set_child_cur(c_table.win, p_cursor, inj_node_info.range.s_row, c_table.indents) else - M.set_child_cur(c_table.win, p_cursor, inj_node_info.range.s_row) + ninjection.set_child_cur(c_table.win, p_cursor, inj_node_info.range.s_row) end ---@type NJLspStatus? @@ -265,7 +261,6 @@ M.edit = function() lsp_status, err = util.start_lsp(inj_node_lang, root_dir) if not lsp_status then if not cfg.suppress_warnings then - err = tostring(err) ---@cast err string vim.notify("ninjection.edit() warning: starting LSP " .. err, vim.log.levels.WARN) -- Don't return early on LSP failure end @@ -279,7 +274,7 @@ M.edit = function() ok, raw_output = pcall(function() return vim.api.nvim_buf_get_var(p_bufnr, "ninjection") end) - if ok then + if ok and type(raw_output) == "table" then p_ninjection = raw_output else err = tostring(raw_output) @@ -305,14 +300,18 @@ M.edit = function() return nil end ---- Function: Replace the original injected language text in the parent buffer ---- with the current buffer text. This state is stored by in the vim.b.ninjection ---- table as an NJParent table in the child, and NJChild table indexed by the +---@tag ninjection.replace() +---@brief +--- Replaces the original injected language text in the parent buffer +--- with the current buffer text. This state is stored by in the `vim.b.ninjection` +--- table as an `NJParent` table in the child, and `NJChild` table indexed by the --- child bufnr in the parent. This relationship is validated before replacing. ----@return nil|string err Returns err string, if applicable -M.replace = function() - ---@type boolean, any?, string?, NJChild?, NJParent?, integer? - local ok, raw_output, err, nj_child_b, nj_p_b, this_bufnr +--- +---@return string? err +--- +ninjection.replace = function() + ---@type boolean, unknown, string?, integer? + local ok, raw_output, err, this_bufnr ok, raw_output = pcall(function() return vim.api.nvim_get_current_buf() @@ -320,14 +319,14 @@ M.replace = function() if not ok then error(tostring(raw_output), 2) end - this_bufnr = raw_output - if not this_bufnr then + if type(raw_output) ~= "number" then error( "ninjection.replace() error: Could not retrieve a buffer handle " .. "calling vim.api.nvim_get_current_buf().", 2 ) end + this_bufnr = raw_output ---@cast this_bufnr integer -- We need to validate that this buffer has a parent buffer, and that the @@ -335,7 +334,7 @@ M.replace = function() ok, raw_output = pcall(function() return vim.api.nvim_buf_get_var(this_bufnr, "ninjection") end) - if not ok then + if not ok or type(raw_output) ~= "table" then err = tostring(raw_output) if err:find("Key not found: ninjection") then if not cfg.suppress_warnings then @@ -345,18 +344,20 @@ M.replace = function() ) end return nil + else + error("ninjection.replace() error: Could not retrieve ninjection table " .. "from child buffer." .. err, 2) end end - nj_child_b = raw_output + ---@type NJChild + local nj_child_b = raw_output if not nj_child_b.p_bufnr then - error("ninjection.replace() error: Could not retrieve valid parent buffer " .. "for this buffer.", 2) + error("ninjection.replace() error: Could not retrieve valid parent buffer for this buffer.", 2) end - ---@cast nj_child_b NJChild ok, raw_output = pcall(function() return vim.api.nvim_buf_get_var(nj_child_b.p_bufnr, "ninjection") end) - if not ok then + if not ok or type(raw_output) ~= "table" then err = tostring(raw_output) if err:find("Key not found: ninjection") then error( @@ -365,43 +366,43 @@ M.replace = function() 2 ) end - error(err, 2) + error("ninjection.replace() error: Could not retrieve ninjection table " .. "for parent buffer." .. err, 2) end - nj_p_b = raw_output + ---@type NJParent + local nj_p_b = raw_output if not vim.tbl_contains(nj_p_b.children, this_bufnr) then - error("ninjection.replace() error: The recorded parent buffer has no " .. "record of this buffer.", 2) + error("ninjection.replace() error: The recorded parent buffer has no record of this buffer.", 2) end ---@cast nj_p_b NJParent ok, raw_output = pcall(function() return vim.api.nvim_win_get_cursor(0) end) - if not ok then + if not ok or type(raw_output) ~= "table" then error(tostring(raw_output), 2) end - ---@type integer[]? + ---@type integer[] local this_cursor = raw_output - if not this_cursor then + if not this_cursor[2] then if not cfg.suppress_warnings then vim.notify( - "ninjection.replace() warning: No child cursor values returned " .. "by vim.api.nvim_win_get_cursor(0)", + "ninjection.replace() warning: No child cursor values returned by vim.api.nvim_win_get_cursor(0)", vim.log.levels.WARN ) end end - ---@cast this_cursor integer[] if not nj_child_b.p_range then - error("ninjection.replace() error: missing parent buffer range values. " .. "Cannot sync changes.", 2) + error("ninjection.replace() error: missing parent buffer range values. Cannot sync changes.", 2) end ok, raw_output = pcall(function() return vim.api.nvim_buf_get_lines(0, 0, -1, false) end) - if not ok then + if not ok or type(raw_output) ~= "table" then error(tostring(raw_output), 2) end - ---@type string[]? + ---@type string[] local rep_text = raw_output if not rep_text or rep_text == "" then if not cfg.suppress_warnings then @@ -412,21 +413,19 @@ M.replace = function() end return nil end - ---@cast rep_text string[] if cfg.preserve_indents then raw_output, err = util.restore_indents(rep_text, nj_child_b.p_indents) - if err then + if not raw_output or type(raw_output) ~= "table" then if not cfg.suppress_warnings then vim.notify( - "ninjection.replace() warning: util.restore_indents() " .. "could not restore indents: " .. err, + "ninjection.replace() warning: util.restore_indents() could not restore indents: " .. err, vim.log.levels.WARN ) end else rep_text = raw_output end - ---@cast rep_text string[] end ok, raw_output = pcall(function() @@ -475,7 +474,7 @@ M.replace = function() end -- Reset the cursor to the same relative position in the parent buffer - ---@type integer[]? + ---@type integer[] local pos if cfg.preserve_indents then pos = { @@ -485,7 +484,7 @@ M.replace = function() else pos = { this_cursor[1] + nj_child_b.p_range.s_row, this_cursor[2] } end - ---@cast pos integer[] + ok, raw_output = pcall(function() return vim.api.nvim_win_set_cursor(0, pos) end) @@ -493,11 +492,11 @@ M.replace = function() err = tostring(raw_output) if not cfg.suppress_warnings then vim.notify( - "ninjection.replace() warning: could not restore cursor " .. "position in the parent buffer." .. err, + "ninjection.replace() warning: could not restore cursor position in the parent buffer." .. err, vim.log.levels.WARN ) end end end -return M +return ninjection diff --git a/lua/ninjection/config.lua b/lua/ninjection/config.lua index 333da56..9e445ab 100644 --- a/lua/ninjection/config.lua +++ b/lua/ninjection/config.lua @@ -1,9 +1,16 @@ ---@module "ninjection.config" +---@brief +--- The config module contains the default ninjection configuration table and +--- functions to merge user config options and reload config changes. +--- local M = {} local vc = require("ninjection.health").validate_config +---@nodoc ---@type Ninjection.Config + +---@tag default_config local default_config = { ---@type string file_lang = "nix", @@ -57,7 +64,31 @@ local default_config = { }, } --- Force reload all ninjection modules to flush caches and apply a new config. +---@nodoc +--- Provide default_config for inspection, primarily for documentation. +---@return Ninjection.Config +M.get_default = function() + return default_config +end + +---@eval return (function() +--- local s = vim.inspect(require("ninjection.config").get_default()) +--- s = s:gsub("\\t", " ") +--- s = s:gsub("\\n", "\n") +--- local lines = vim.split(s, "\n") +--- for i, line in ipairs(lines) do +--- lines[i] = "`" .. line -- Prefix each line with a backtick. +--- end +--- return lines +--- end)() +---@minidoc_afterlines_end + +---@tag config.reload() +---@brief +--- Reloads all ninjection modules to flush caches and apply a new config. +--- +---@return nil +--- M.reload = function() for key in pairs(package.loaded) do if key:match("^ninjection") then @@ -66,6 +97,10 @@ M.reload = function() end end +---@nodoc +--- Merges user provided configuration overrides with the default configuration. +---@return nil +--- local function merge_config() ---@type Ninjection.Config local user_config = (type(vim.g.ninjection) == "function" and vim.g.ninjection() or vim.g.ninjection) or {} diff --git a/lua/ninjection/health.lua b/lua/ninjection/health.lua index a690bc8..514e8d9 100644 --- a/lua/ninjection/health.lua +++ b/lua/ninjection/health.lua @@ -1,5 +1,8 @@ ---@module "ninjection.health" - +---@brief +--- The health module contains functions to validate configuration parameters +--- and check for required dependencies. +--- local health = require("vim.health") local start = health.start local ok = health.ok @@ -8,8 +11,16 @@ local h_error = health.error local M = {} +---@tag ninjection.health.validate_config() +---@brief +--- Validates either a provided configuration table or the +--- current configuration. +--- +--- Parameters ~ ---@param cfg? Ninjection.Config ----@return boolean, string|nil +--- +---@return boolean is_valid, string? err +--- M.validate_config = function(cfg) cfg = cfg or require("ninjection.config").cfg ---@type boolean, string? @@ -33,7 +44,6 @@ M.validate_config = function(cfg) return is_valid, err end - local required_plugins = { { lib = "lspconfig", optional = false, info = "Required for LSP integration" }, { lib = "nvim-treesitter", optional = false, info = "Required for injected language parsing" }, diff --git a/lua/ninjection/treesitter.lua b/lua/ninjection/treesitter.lua index 2cb1cfe..65e4be8 100644 --- a/lua/ninjection/treesitter.lua +++ b/lua/ninjection/treesitter.lua @@ -1,19 +1,26 @@ ---@module "ninjection.treesitter" - +---@brief +--- The treesitter module contains all treesitter related functions for ninjection. +--- local M = {} local cfg = require("ninjection.config").cfg local ts = require("vim.treesitter") ---- Function: Get a parsed query from Treesitter given a language and pattern. ----@param query string Lua-literal string for Treesitter query. ----@param lang? string|nil Default: "nix", language grammar to parse with. ----@return vim.treesitter.Query|nil parsed_query The parsed Treesitter Query object ----@return nil|string err Error string, if applicable +---@tag ninjection.treesitter.qet_query() +---@brief +--- Retrieves a parsed query from Treesitter given a language and pattern. +--- +--- Parameters ~ +---@param query string - Lua-literal string for Treesitter query. +---@param lang? string? - Default: `"nix"` - language grammar to parse with. +--- +---@return vim.treesitter.Query? parsed_query, string? err +---The parsed Treesitter Query object +--- M.get_query = function(query, lang) - ---@type string|nil lang = lang or "nix" ---@cast lang string - ---@type boolean, any|nil, vim.treesitter.Query|nil + ---@type boolean, unknown, vim.treesitter.Query? local ok, raw_output, parsed_query ok, raw_output = pcall(function() @@ -22,8 +29,7 @@ M.get_query = function(query, lang) if not ok then error(tostring(raw_output), 2) end - parsed_query = raw_output - if not parsed_query then + if not raw_output.query then if cfg.suppress_warnings == false then vim.notify( "ninjection.treesitter.get_query() warning: No Query result " @@ -33,29 +39,34 @@ M.get_query = function(query, lang) end return nil end + parsed_query = raw_output ---@cast parsed_query vim.treesitter.Query return parsed_query end ---- Function: Parses the root tree for a language in a buffer. +---@tag ninjection.treesitter.get_root() +---@brief +--- Parses the root tree for a language in a buffer. +--- +--- Parameters ~ +---@param bufnr integer - Handle for buffer to parse. +---@param lang? string - Default: `"nix"` - language to parse with. +--- +---@return TSNode? root, string? err +--- Root node of the TSTree for the language. --- ----@param bufnr integer Handle for buffer to parse. ----@param lang? string Default: "nix" language to parse with. ----@return TSNode|nil root root node of the TSTree for the language. ----@return nil|string err Error string, if applicable. M.get_root = function(bufnr, lang) lang = lang or "nix" - ---@type boolean, any|nil, vim.treesitter.LanguageTree|nil + ---@type boolean, unknown, vim.treesitter.LanguageTree? local ok, raw_output, parser ok, raw_output = pcall(function() - return vim.treesitter.get_parser(bufnr, "nix") + return vim.treesitter.get_parser(bufnr, lang) end) if not ok then error("ninjection.treesitter.get_root() error: " .. tostring(raw_output), 2) end - parser = raw_output - if not parser then + if not raw_output then if cfg.suppress_warnings == false then vim.notify( "ninjection.treesitter.get_root() warning: No parser available " .. "for: " .. lang, @@ -64,9 +75,10 @@ M.get_root = function(bufnr, lang) end return nil end + parser = raw_output ---@cast parser vim.treesitter.LanguageTree - ---@type TSTree|nil + ---@type TSTree? local tree = parser:parse()[1] if not tree then if cfg.suppress_warnings == false then @@ -79,7 +91,7 @@ M.get_root = function(bufnr, lang) end ---@cast tree TSTree - ---@type TSNode|nil + ---@type TSNode? local root = tree:root() if not root then if cfg.suppress_warnings == false then @@ -94,21 +106,25 @@ M.get_root = function(bufnr, lang) return root end ---- Function: Identify the injected language node at the current cursor position +---@tag ninjection.treesitter.get_node_table() +---@brief +--- Identifies the injected language node at the current cursor position --- with start and ending coordinates. --- ----@param query string Pattern to identify an injected lang. ----@param lang? string Default: "nix" language grammar to use for parsing. ----@return NJNodeTable|nil table ---- Return: On success, a table containing: ---- node: TSNode - the Treesitter node element (see :h TSNode). ---- range: NJRange - s_col, s_row, e_col, e_row, integer coordinates for node. ---- NOTE: Coordinates may not match the actual text locations (see: ---- get_visual_range() for this). ----@return nil|string err Error string, if applicable +--- Parameters ~ +---@param query string - Pattern to identify an injected lang. +---@param lang? string - Default: `"nix"` language grammar to use for parsing. +--- +---@return NJNodeTable? table, string? err +--- Returns a table containing: +--- - node: `TSNode` - the Treesitter node element (see :h TSNode). +--- - range: `NJRange` - row/col ranges for the node. +--- NOTE: Coordinates may not match the actual text locations +--- (see: `ninjection.treesitter.get_visual_range()` for this). +-- M.get_node_table = function(query, lang) lang = lang or "nix" - ---@type boolean, any|nil, string|nil, integer|nil, integer[]|nil + ---@type boolean, unknown, string?, integer?, integer[]? local ok, raw_output, err, bufnr, cursor ok, raw_output = pcall(function() @@ -117,13 +133,10 @@ M.get_node_table = function(query, lang) if not ok then error("ninjection.treesitter.get_node_table() error: " .. tostring(raw_output), 2) end - bufnr = raw_output - if not bufnr then - error( - "ninjection.treesitter.get_node_table() error: No buffer handle " .. "returned: " .. tostring(raw_output), - 2 - ) + if type(raw_output) ~= "number" then + error("ninjection.treesitter.get_node_table() error: No buffer handle returned: " .. tostring(raw_output), 2) end + bufnr = raw_output ---@cast bufnr integer ok, raw_output = pcall(function() @@ -132,8 +145,7 @@ M.get_node_table = function(query, lang) if not ok then error("ninjection.treesitter.get_node_table() error: " .. tostring(raw_output), 2) end - cursor = raw_output - if not cursor then + if type(raw_output) ~= "table" then if cfg.suppress_warnings == false then vim.notify( "ninjection.treesitter.get_node_table() warning: Could not " @@ -144,6 +156,7 @@ M.get_node_table = function(query, lang) end return nil end + cursor = raw_output ---@cast cursor integer[] ---@type integer @@ -151,7 +164,7 @@ M.get_node_table = function(query, lang) ---@type integer local cur_col = cursor[2] - --- @type vim.treesitter.Query|nil + --- @type vim.treesitter.Query? local parsed_query parsed_query, err = M.get_query(query, lang) if not parsed_query then @@ -161,7 +174,7 @@ M.get_node_table = function(query, lang) vim.log.levels.WARN ) end - return nil + return nil, err end ---@cast parsed_query vim.treesitter.Query @@ -175,7 +188,7 @@ M.get_node_table = function(query, lang) vim.log.levels.WARN ) end - return nil + return nil, err end ---@cast root TSNode @@ -217,13 +230,18 @@ M.get_node_table = function(query, lang) return nil end ---- Function: Parse an injected content node for an associated language comment. +---@tag ninjection.treesitter.get_inj_lang() +---@brief +--- Parse an injected content node for an associated language comment. +--- +--- Parameters ~ +---@param query string - Query to identify an injected content node. +---@param bufnr integer - Handle for the buffer to query in. +---@param file_lang? string - Default: `"nix"` - Parent file language to find +--- injections in. +--- +---@return string? inj_lang , string? err - Injected language identified. --- ----@param query string Query to identify an injected content node. ----@param bufnr integer Handle for the buffer to query in. ----@param file_lang? string Default: "nix". Parent file language to find injections in. ----@return string|nil inj_lang Injected language identified. ----@return nil|string err Error string, if applicable. M.get_inj_lang = function(query, bufnr, file_lang) ---@type boolean, any|nil, string|nil, vim.treesitter.Query|nil local ok, raw_output, err, parsed_query @@ -379,20 +397,26 @@ end -- Treesitter's selection for "injected.content" doesn't match the actual text -- selected. We need a function that adjusts the selection to match. ---- Function: Gets an adjusted "visual" range for a node by approximating the +---@tag ninjection.treesitter.get_visual_range() +---@brief +--- Gets an adjusted "visual" range for a node by approximating the --- range of text that is actually seen (as returned by get_node_text). --- This makes an opinionated assumption about formatting that expects: ---- assigment = # injected_lang_comment ---- '' ---- injected.content ---- ''; +--- +--- `assigment = # injected_lang_comment +--- `'' +--- ` injected.content +--- `''; +--- --- The '' and ''; characters are not important, but the dedicated lines for --- comment delimiters and the language comment above that block are important. --- ---- @param node TSNode The Treesitter node to select in. ---- @param bufnr integer Handle for the buffer to work in. ---- @return NJRange|nil vs_range Range of text selected. ---- @return nil|string err Error string, if applicable. +--- Parameters ~ +---@param node TSNode - The Treesitter node to select in. +---@param bufnr integer - Handle for the buffer to work in. +--- +---@return NJRange? vs_range, string? err - Range of text selected. +--- M.get_visual_range = function(node, bufnr) ---@type boolean, any|nil, string|nil, integer[]|nil local ok, raw_output, err, range diff --git a/lua/ninjection/types.lua b/lua/ninjection/types.lua index 933930a..b05ee7f 100644 --- a/lua/ninjection/types.lua +++ b/lua/ninjection/types.lua @@ -1,132 +1,147 @@ ----@meta +---@module "ninjection.types" +---@brief +--- The types module contains all ninjection specific type definitions. +--- +---@meta +---@tag EditorStyle ---@alias EditorStyle "cur_win" | "floating" | "v_split" | "h_split" --- Supported injected language editor window methods. +---@brief +--- Supported window styles for buffer editor: +--- `"cur_win" | "floating" | "v_split" | "h_split"` +--- ---@alias lspconfig.Config.command {[1]:string|vim.api.keyset.user_command} --- Modified from nvim-lspconfig/lua/lspconfig/configs.lua because I can't find --- a reference to: vim.api.keyset.create_user_command.command_args - --- Ninjection configuration type annotations. +---@brief +--- Modified from `nvim-lspconfig/lua/lspconfig/configs.lua` because I can't +--- find a reference to: `vim.api.keyset.create_user_command.command_args` +--- +---@tag Ninjection.Subcommand +---@class Ninjection.Subcommand +---@brief Implemented by `plugin/ninjection.lua` for user commands. +--- +---@field impl fun() +---@field complete? fun(arg_lead: string): string[] +--- +---@tag Ninjection.CmdOpts +---@class Ninjection.CmdOpts +---@brief Implemented by `plugin/ninjection.lua` for user commands. +--- +---@field args string - The entire argument string as typed. +---@field fargs string[] - Command arguments as an array of strings. +---@field bang? boolean - Bang (!) flag. +---@field line1? number - Starting line number for optional range. +---@field line2? number - Ending line number for optional range. +---@field count? number - Optional count. +--- +---@tag Ninjection.Config ---@class Ninjection.Config ----@field file_lang? string (optional) -- default: "nix" --- Native file type to search for injected languages in. --- Must have a matching entry in inj_lang_queries. --- Currently only supports nix, but could be extended. ----@field preserve_indents? boolean (optional) -- default: true --- Re-apply indents from the parent buffer. --- This option should be used in conjunction with auto_format because --- This will re-apply indents that auto_format normally removes. --- If you don't remove them, then they will be re-applied which will increase --- the original indenation. ----@field auto_format? boolean (optional) -- default: false --- Whether to auto format the new child buffer. ----@field format_cmd? string (optional) --- Command for auto_format ----@field injected_comment_lines? integer (optional) -- default: 1 --- Offset comment delimiting lines based on style preferences. --- For example, offsetting 1 line would function with this format: --- # injected_lang --- '' --- injected content --- ''; --- --- Offsetting 0 lines would function with this format: --- # injected_lang --- ''injected content --- more injected content --- end content''; ----@field register? string (optional) -- default: "z" --- Register to use to copy injected content. ----@field suppress_warnings boolean (optional) -- default: false --- If true, Ninjection will only show critical errors. --- If ninjection is not functioning properly, ensure this is false --- for debugging. ----@field editor_style EditorStyle (optional) -- default: "floating" --- Window style to use for the injected context editor: --- "cur_win" - edit in the current buffer's window, --- "floating" - open a new floating window, --- "v_split" - edit in a new vertically split window, --- "h_split" - edit in a new horizontally split window ----@field inj_lang_queries table (optional) -- default: --- { --- nix = [[ --- ( --- (comment) @injection.language --- . --- [ --- (indented_string_expression --- (string_fragment) @injection.content) --- (string_expression --- (string_fragment) @injection.content) --- ] --- (#gsub! @injection.language "#%s*([%w%p]+)%s*" "%1") --- (#set! injection.combined) --- ) --- ]], --- }, --- Contains per-language string literals for Treesitter queries to Identify --- injected content nodes. ----@field inj_lang_query string (dynamic) --- This is configured by referencing file_lang in the table of --- inj_lang_queries. This cannot be nil, and should be tested. ----@field lsp_map table (optional) default: --- lsp_map = { --- bash = "bashls", --- c = "clangd", --- cpp = "clangd", --- javascript = "ts_ls", --- json = "jsonls", --- lua = "lua_ls", --- python = "ruff", --- rust = "rust_analyzer", --- sh = "bashls", --- typescript = "ts_ls", --- yaml = "yamlls", --- zig = "zls", --- }, --- LSPs associated with injected languages. The keys must match the language --- comment used to identify injected languages, and the value must match the --- LSP configured in your lspconfig. - +---@brief Implemented by `ninjection/config.lua` for default and user configs. +--- +---@field file_lang? string - File type to search for injected languages in. +--- Must have a matching entry in `inj_lang_queries`. +--- Currently only supports nix, but could be extended. +--- +---@field preserve_indents? boolean - Preserve indents from the parent buffer. +--- This option should be used in conjunction with `auto_format` because this +--- will re-apply indents that `auto_format` normally removes. If you don't remove +--- indents, then enabling this will increas the original indenation. +--- +---@field auto_format? boolean - Auto format the new child buffer. +---@field format_cmd? string - Command for `auto_format`. +---@field injected_comment_lines? integer - The offset for comment delimiting +--- lines. For example, offsetting 1 line would function with this format: +--- +--- `# injected_lang +--- `'' +--- ` injected content +--- `''; +--- +--- Offsetting 0 lines would function with this format: +--- +--- `# injected_lang +--- `''injected content +--- `more injected content +--- `end content''; +--- +---@field register? string - Register to use to copy injected content. +---@field suppress_warnings boolean - Suppress warnings. +--- NOTE: If ninjection is not functioning properly, ensure this is false for +--- debugging. +--- +---@field editor_style EditorStyle +--- Window style to use for the injected context editor. +--- +---@field inj_lang_queries table - Contains per-language string +--- literals for Treesitter queries to Identify injected content nodes. +--- +---@field inj_lang_query string This is configured by referencing `file_lang` +--- in the table of `inj_lang_queries`. This cannot be nil. +--- +---@field lsp_map table - LSP associated with the injected +--- languages These keys must match the language comment used to identify +--- injected languages, and the value must match the LSP configured in your +--- lspconfig. +--- +---@tag NJRange ---@class NJRange +---@brief Store cursor position coordinates. +--- ---@field s_row integer ---@field s_col integer ---@field e_row integer ---@field e_col integer - +--- +---@tag NJNodeTable ---@class NJNodeTable +---@brief Store a Treesitter node and its associated coordinates. +--- ---@field node TSNode ---@field range NJRange - +--- +---@tag NJIndents ---@class NJIndents +---@brief Store indents for a text buffer. +--- ---@field t_indent number ---@field b_indent number ---@field l_indent number - +--- +---@tag NJParent +---@brief Store associated child bufnrs. ---@class NJParent ---@field children integer[] - +--- +---@tag NJChild ---@class NJChild +---@brief Store associated parent buffer information. +--- ---@field bufnr integer ---@field root_dir string ---@field p_bufnr integer ---@field p_indents NJIndents ---@field p_range NJRange - +--- +---@tag NJLspStatus ---@class NJLspStatus ----@field status string -- The LSP startup status. Possible values: "unmapped", ---- "unconfigured", "unavailable", "no-exec", "unsupported", "failed_start", "started" ----@field client_id integer -- The client ID of the started LSP, or -1 on failure - --- Helper annotation for lspconfig from nvim-lspconfig/lua/lspconfig/configs.lua ---- @class lspconfig.Config : vim.lsp.ClientConfig ---- @field enabled? boolean ---- @field single_file_support? boolean ---- @field silent? boolean ---- @field filetypes? string[] ---- @field filetype? string ---- @field on_new_config? fun(new_config: lspconfig.Config?, new_root_dir: string) ---- @field autostart? boolean ---- @field package _on_attach? fun(client: vim.lsp.Client, bufnr: integer) ---- @field root_dir? string|fun(filename: string, bufnr: number) ---- @field commands? table +---@brief Store LSP status and associated client ID. +--- +---@field status string - The LSP startup status. Possible values: `"unmapped"`, +--- `"unconfigured"`, `"unavailable"`, `"no-exec"`, `"unsupported"`, `"failed_start"`, +--- `"started"` +--- +---@field client_id integer - The client ID of the started LSP, -1 on failure +--- +---@tag lspconfig.Config +---@class lspconfig.Config : vim.lsp.ClientConfig +---@brief Annotation for lspconfig from `nvim-lspconfig/lua/lspconfig/configs.lua` +--- +---@field enabled? boolean +---@field single_file_support? boolean +---@field silent? boolean +---@field filetypes? string[] +---@field filetype? string +---@field on_new_config? fun(new_config: lspconfig.Config?, new_root_dir: string) +---@field autostart? boolean +---@field package _on_attach? fun(client: vim.lsp.Client, bufnr: integer) +---@field root_dir? string|fun(filename: string, bufnr: number) +---@field commands? table diff --git a/lua/ninjection/util.lua b/lua/ninjection/util.lua index 850d3dc..1c20a62 100644 --- a/lua/ninjection/util.lua +++ b/lua/ninjection/util.lua @@ -1,6 +1,13 @@ ---@module "ninjection.util" - +---@brief +--- The util module contains helper functions utilized by the main ninjection +--- module for getting and recording indentation, creating new child buffers, +--- creating new windows, setting cursor position, and starting and attaching +--- the appropriate LSP to the child buffer. +--- local M = {} +---@nodoc +---@type Ninjection.Config local cfg = require("ninjection.config").cfg local lspconfig = require("lspconfig") @@ -8,17 +15,21 @@ local lspconfig = require("lspconfig") -- buffer to allow easily formatting the buffer without worrying about its -- relative placement in the parent buffer. ----Function: Find whitespace indents (top, bottom, left) in the provided buffer. ----@param bufnr integer Buffer handle ----@return NJIndents|nil table Stores indentation values ----@return nil|string err Error string, if applicable ---- Return, on success, A table containing: ---- - t_indent: number of blank lines at the top. ---- - b_indent: number of blank lines at the bottom. ---- - l_indent: minimum number of leading spaces on nonempty lines. ---- Return, on failure, nil and error string, if applicable +---@tag ninjection.util.get_indents() +---@brief +--- Finds whitespace indents (top, bottom, left) in the provided buffer. +--- +--- Parameters ~ +---@param bufnr integer - Buffer handle. +--- +---@return NJIndents? indents, string? err +--- Returns, on success, a table containing: +--- - `t_indent`: number of blank lines at the top. +--- - `b_indent`: number of blank lines at the bottom. +--- - `l_indent`: minimum number of leading spaces on nonempty lines. +--- M.get_indents = function(bufnr) - ---@type boolean, any|nil, string[]|nil + ---@type boolean, unknown, string[] local ok, raw_output, lines ok, raw_output = pcall(function() @@ -27,8 +38,13 @@ M.get_indents = function(bufnr) if not ok then error(tostring(raw_output), 2) end + if type(raw_output) ~= "table" then + error("ninjection error: Expected vim.api.nvim_buf_get_lines() to return a table.", 2) + end + ---@cast raw_output string[] lines = raw_output - if not lines or #lines == 0 then + + if #lines == 0 then if cfg.suppress_warnings == false then vim.notify( "ninjection.util.get_indents() warning: No lines returned " @@ -63,11 +79,12 @@ M.get_indents = function(bufnr) for _, line in ipairs(lines) do ---@cast line string if not line:match("^%s*$") then - ---@type string|nil + ---@type string? local indent = line:match("^(%s*)") if indent then -- Use vim.fn.strdisplaywidth() to calculate the visible width of the indent, -- which will account for tabs. + ---@type integer local count = vim.fn.strdisplaywidth(indent) if count < indents.l_indent then indents.l_indent = count @@ -83,17 +100,21 @@ M.get_indents = function(bufnr) return indents, nil end +---@tag ninjection.util.restore_indents() +---@brief --- Restores the recorded whitespace indents (top, bottom, and left indent) ---- to a block of text. +--- for the provided text. --- ---- @param text string|table The text to restore indents in. +--- Parameters ~ +---@param text string|table The text to restore indents to. --- Can be either a string (with newline separators) or a table of lines. ---- @param indents NJIndents Table with indent values for t, b, l ---- @return table|nil restored_lines A table of lines with ---- the indents restored. ---- @return nil|string err Error message, if applicable +---@param indents NJIndents Table with indent values for t, b, l +--- +---@return string[]? restored_lines, string? err +--- Lines with the indents restored. +--- M.restore_indents = function(text, indents) - ---@type boolean, any|nil, table|nil + ---@type boolean, unknown, string[]? local ok, raw_output, lines if type(text) == "string" then @@ -103,8 +124,12 @@ M.restore_indents = function(text, indents) if not ok then error(tostring(raw_output), 2) end + if type(raw_output) ~= "table" then + error("ninjection error: Expected a table returned from vim.split()", 2) + end + ---@cast raw_output string[] lines = raw_output - if not lines then + if #lines == 0 then if cfg.suppress_warnings == false then vim.notify( "ninjection.util.restore_indents() warning: No lines " .. "returned from calling vim.split()", @@ -118,7 +143,7 @@ M.restore_indents = function(text, indents) else error("ninjection.util.restore_indents() error: Text must be a string or " .. "a table of lines", 2) end - ---@cast lines table + ---@cast lines string[] -- Create the left indentation string. ---@type string @@ -154,35 +179,45 @@ M.restore_indents = function(text, indents) return lines end --- Function: Open a vertical or horizontal split window for the child buffer. ----@param split_cmd string vsplit or split. +---@nodoc +--- Opens a vertically or horizontally split window for the child buffer. +---@param split_cmd string v_split or split. ---@param bufnr integer child bufnr. ----@return integer|nil winid Handle for new window. +---@return integer winid Handle for new window. local function open_split_win(split_cmd, bufnr) - ---@type boolean, any|nil, integer|nil + ---@type boolean, unknown, integer|nil local ok, raw_output, winid vim.cmd(split_cmd) ok, winid = pcall(vim.api.nvim_get_current_win) if not ok or not winid then - error("create_child_win() error: no handle returned for window: " .. tostring(winid), 2) + error( + "ninjection error: vim.api.nvim_get_current_win() did not return a " .. " window handle." .. tostring(winid), + 2 + ) end ok, raw_output = pcall(function() return vim.api.nvim_win_set_buf(winid, bufnr) end) if not ok then - error("create_child_win() error: failed to set buffer in new window: " .. tostring(raw_output), 2) + error( + "ninjection error: vim.api.nvim_win_set_buf() failed to set buffer " + .. "in new window: " + .. tostring(raw_output), + 2 + ) end ---@cast winid integer return winid end --- Function: Set the child window cursor to the same relative position as it was --- in the parent. +---@nodoc +--- Creates a window for the provided child buffer with either floating, v_split +--- or h_split styles. ---@param bufnr integer The buffer to create a viewport for. ---@param style EditorStyle The window style to edit the buffer with. ----@return integer winid Default: 0, child window handle, if created. +---@return integer winid Default: 0 (cur_win), child window handle, if created. local function create_child_win(bufnr, style) - ---@type boolean, any|nil, integer|nil + ---@type boolean, unknown, integer local ok, raw_output, winid if style == "floating" then @@ -192,7 +227,7 @@ local function create_child_win(bufnr, style) local row = math.floor((vim.o.lines - height) / 2) local col = math.floor((vim.o.columns - width) / 2) - ---@type table + ---@type vim.api.keyset.win_config local opts = { style = "minimal", relative = "editor", -- relative to the whole editor @@ -209,12 +244,11 @@ local function create_child_win(bufnr, style) if not ok then error(tostring(raw_output), 2) end - ---@type integer|nil - winid = raw_output - if not winid then - error("create_child_win() error: no handle returned for window: " .. tostring(raw_output), 2) + if type(raw_output) ~= "number" then + error("ninjection error: vim.api.nvim_open_win() did not return a window " .. "handle.") end - ---@cast winid integer + ---@cast raw_output integer + winid = raw_output return winid elseif style == "v_split" then winid = open_split_win("vsplit", bufnr) @@ -230,19 +264,24 @@ local function create_child_win(bufnr, style) return 0 end --- Function: Create a child buffer and window to edit injected language text. ----@param p_bufnr integer Buffer handle for parent buffer. ----@param p_name string Name for parent buffer. ----@param p_range NJRange Text range for the injected text. ----@param root_dir string Root directory for project, or cwd. ----@param text string Text to populate the child buffer with. ----@param lang string Language to configure buffer for. ----@return {bufnr: integer|nil, win: integer|nil, indents: NJIndents} c_table --- containing handles for the child buffer and window, if available, and parent --- indents. ----@return string|nil err Error string, if applicable. +---@tag ninjection.util.create_child_buf() +---@brief +--- Creates a child buffer to edit injected language text. +--- +--- Parameters ~ +---@param p_bufnr integer - Buffer handle for parent buffer. +---@param p_name string - Name for parent buffer. +---@param p_range NJRange - Text range for the injected text. +---@param root_dir string - Root directory for project, or cwd. +---@param text string - Text to populate the child buffer with. +---@param lang string - Language to configure buffer for. +--- +---@return { bufnr: integer?, win: integer?, indents: NJIndents } c_table, string? err +-- Returns table containing handles for the child buffer and window, if +-- available, and parent indents. +-- M.create_child_buf = function(p_bufnr, p_name, p_range, root_dir, text, lang) - ---@type boolean, any|nil, string|nil, integer|nil + ---@type boolean, unknown, string?, integer? local ok, raw_output, err, c_bufnr ok, raw_output = pcall(function() @@ -253,7 +292,7 @@ M.create_child_buf = function(p_bufnr, p_name, p_range, root_dir, text, lang) end vim.notify("ninjection.edit(): Copied injected content text to register: " .. cfg.register, vim.log.levels.INFO) - ---@type integer|nil + ---@type integer? c_bufnr = vim.api.nvim_create_buf(true, true) if not c_bufnr then error("ninjection.edit() error: Failed to create a child buffer.", 2) @@ -293,7 +332,7 @@ M.create_child_buf = function(p_bufnr, p_name, p_range, root_dir, text, lang) -- Preserve indentation after creating and pasting buffer contents, before -- autoformatting, or they will be lost. - ---@type NJIndents|nil + ---@type NJIndents? local p_indents if cfg.preserve_indents then p_indents, err = M.get_indents(0) @@ -358,17 +397,22 @@ M.create_child_buf = function(p_bufnr, p_name, p_range, root_dir, text, lang) return { bufnr = c_bufnr, win = c_win, indents = p_indents } end --- Function: Set the child cursor to the same relative position as in the --- parent window. +---@tag ninjection.util.set_child_cur() +---@brief +--- Sets the child cursor to the same relative position as in the parent window. +--- +--- Parameters ~ ---@param c_win integer Handle for child window to set the cursor in. ---@param p_cursor integer[] Parent cursor pos. ---@param s_row integer Starting row from the parent to offset the child cursor by. ---@param indents NJIndents? Indents to calculate additional offsets with. ----@return nil|string err Error string, if applicable. +--- +---@return string? err +--- M.set_child_cur = function(c_win, p_cursor, s_row, indents) - ---@type boolean, any|nil, string|nil + ---@type boolean, unknown, string? local ok, raw_output, err - ---@type integer[]|nil + ---@type integer[]? local offset_cur -- Assuming autoformat will remove any existing indents, we need to offset -- the cursor for the removed indents. @@ -413,18 +457,21 @@ end -- Autocommands don't trigger properly when creating and arbitrarily assigning -- filetypes to buffers, so we need a function to start the appropriate LSP. ---- Start an appropriate LSP for the provided language ---- @param lang string The filetype of the injected language (e.g., "lua", "python"). ---- @param root_dir string The root directory for the buffer. ---- @return NJLspStatus|nil result A table containing the LSP status and client_id ---- Return: "unmapped", "unconfigured", "unavailable", "no-exec", "unsupported", ---- "failed_start", "started" and client_id if available ---- @return nil|string err Error message, if applicable +---@tag ninjection.util.start_lsp() +---@brief +--- Starts an appropriate LSP for the provided language. +--- +--- Parameters ~ +---@param lang string - The filetype of the injected language (e.g., "lua", "python"). +---@param root_dir string - The root directory for the buffer. +--- +---@return NJLspStatus? result, string? err - The LSP status. +--- M.start_lsp = function(lang, root_dir) - ---@type boolean, any|nil, string|nil, string|nil + ---@type boolean, unknown, string? local ok, raw_output, lang_lsp - -- The injected langauge must be mapped to an LSP value + -- The injected language must be mapped to an LSP lang_lsp = cfg.lsp_map[lang] if not lang_lsp then vim.notify( @@ -445,7 +492,7 @@ M.start_lsp = function(lang, root_dir) if not ok then error(tostring(raw_output), 2) end - ---@type lspconfig.Config|nil + ---@type lspconfig.Config? local lsp_def = raw_output if not lsp_def then vim.notify( @@ -462,7 +509,7 @@ M.start_lsp = function(lang, root_dir) -- The LSP binary path must exist -- RPC function support is not implemented - ---@type string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient|nil + ---@type string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient? local lsp_cmd = lsp_def.cmd if not lsp_cmd or #lsp_cmd == 0 then vim.notify( @@ -515,7 +562,7 @@ M.start_lsp = function(lang, root_dir) if not ok then error(tostring(raw_output), 2) end - ---@type integer|nil + ---@type integer? local client_id = raw_output if client_id == nil then vim.notify( diff --git a/plugin/ninjection.lua b/plugin/ninjection.lua index bde93c8..273ccf7 100644 --- a/plugin/ninjection.lua +++ b/plugin/ninjection.lua @@ -1,7 +1,5 @@ ----@class NinjectionSubcommand ----@field impl fun() ----@field complete? fun(arg_lead: string): string[] - +---@nodoc +---@type table local subcommand_tbl = { edit = { impl = function() @@ -20,11 +18,17 @@ local subcommand_tbl = { }, } +---@nodoc +---@param opts Ninjection.CmdOpts +---@return nil local function ninjection_cmd(opts) + ---@type string[] local fargs = opts.fargs + ---@type string local subcommand_key = fargs[1] if not subcommand_key or not subcommand_tbl[subcommand_key] then + ---@type string local available = table.concat(vim.tbl_keys(subcommand_tbl), ", ") vim.notify( "Ninjection: Unknown subcommand: " .. tostring(subcommand_key) .. ". Available subcommands: " .. available, @@ -40,6 +44,8 @@ vim.api.nvim_create_user_command("Ninjection", ninjection_cmd, { nargs = 1, -- exactly one argument: the subcommand desc = "Ninjection plugin command with subcommand support", bang = false, + ---@param arg_lead string + ---@return string[] complete = function(arg_lead) local keys = vim.tbl_keys(subcommand_tbl) return vim.tbl_filter(function(key) @@ -57,3 +63,11 @@ end, { noremap = true, silent = true }) vim.keymap.set("n", "(NinjectionSelect)", function() require("ninjection").select() end, { noremap = true, silent = true }) + +---@mod ninjection-command USER COMMAND +---@brief :Ninjection +--- +--- Subcommands: +--- edit => |ninjection.edit| +--- replace => |ninjection.replace| +--- select => |ninjection.select| diff --git a/scripts/gendoc.sh b/scripts/gendoc.sh new file mode 100755 index 0000000..00374c1 --- /dev/null +++ b/scripts/gendoc.sh @@ -0,0 +1,4 @@ +#! /bin/sh +nvim --headless -c 'luafile ./mini-doc.lua' -c 'qa!' && +cat introduction.txt ../doc/ninjection.txt > ../doc/ninjection_final.txt && +mv ../doc/ninjection_final.txt ../doc/ninjection.txt diff --git a/scripts/introduction.txt b/scripts/introduction.txt new file mode 100644 index 0000000..2629928 --- /dev/null +++ b/scripts/introduction.txt @@ -0,0 +1,37 @@ +============================================================================== +*INTRODUCTION* *ninjection.nvim* + +Ninjection is a plugin designed to improve editing injected language text. +Its goal is to provide a seamless, first-class editing experience for injected +code with full support for LSPs, code-snippets, completions, formatting, etc. + +Ninjection utilizes Treesitter's language parsing functionality to identify +nodes that contain an injected language, and appropriately designate that +language. It provides functions to create a new buffer for that language, +and to attach an appropriate LSP to that buffer. + +While Ninjection was written primarily to edit injected languages in Nix files, +it should be easily extensible to other languages. Ninjection provides +configuration options to modify language parsing queries, LSP mappings, +window styles, and formatting. + +Getting started with ninjection: + 1. Run `:checkhealth ninjection` to check that all dependencies are present + and that your configuration is valid. + 2. Move the cursor to an injected code block: + `:Ninjection select` - should highlight the entire injected code block. + `:Ninjection edit` - should open a floating window with the injected text + and attach the appropriate LSP. + `:Ninjection restore` - should apply any changes in the editing buffer to + the original buffer. + You can also test functionality with: + `:lua require("ninjection.ninjection").select()` + `:lua require("ninjection.ninjection").edit()` + `:lua require("ninjection.ninjection").replace()` + 3. Ninjection provides keymap plugs for you to map keybindings + of your choosing: + `(NinjectionEdit)` + `(NinjectionReplace)` + `(NinjectionSelect)` + + diff --git a/scripts/mini-doc.lua b/scripts/mini-doc.lua new file mode 100644 index 0000000..c1a5400 --- /dev/null +++ b/scripts/mini-doc.lua @@ -0,0 +1,29 @@ +require("mini.doc").setup({ + hooks = { + block_pre = function(block) + -- Mark the block if any section has nodoc + for _, section in ipairs(block) do + if section.info.id == "@nodoc" then + block.info.nodoc = true + break + end + end + end, + block_post = function(block) + -- Clear the block if marked for nodoc. + if block.info.nodoc then + block:clear_lines() + end + end, + }, +}) + +local input_files = { + "../plugin/ninjection.lua", + "../lua/ninjection.lua", + "../lua/ninjection/config.lua", + "../lua/ninjection/types.lua", + "../lua/ninjection/health.lua", + "../lua/ninjection/treesitter.lua", +} +require("mini.doc").generate(input_files, "../doc/ninjection.txt")