From db9d1318ad72399abcad6e91f8c7b261c47cb42e Mon Sep 17 00:00:00 2001 From: Jannik Buhr <17450586+jmbuhr@users.noreply.github.com> Date: Sun, 11 Feb 2024 13:44:10 +0100 Subject: [PATCH 1/7] fix: decrement end line number of code content node range by 1 (#88) fix: decrement end line number of code content node range by 1. fix #87. testing: add R chunk to qmd example testing: add norg example testing: add ts example testing: document debug-example script --- lua/otter/keeper.lua | 6 ++++-- tests/debug-example.sh | 16 ++++++++++++++++ tests/examples/{01.lua => 00.lua} | 0 tests/examples/01.qmd | 7 +++++++ tests/examples/03.md | 17 +++++++++++++++++ tests/examples/05.norg | 26 ++++++++++++++++++++++++++ tests/examples/06.ts | 16 ++++++++++++++++ 7 files changed, 86 insertions(+), 2 deletions(-) create mode 100755 tests/debug-example.sh rename tests/examples/{01.lua => 00.lua} (100%) create mode 100644 tests/examples/05.norg create mode 100644 tests/examples/06.ts diff --git a/lua/otter/keeper.lua b/lua/otter/keeper.lua index 77f574f..0143f21 100644 --- a/lua/otter/keeper.lua +++ b/lua/otter/keeper.lua @@ -139,17 +139,19 @@ M.get_current_language_context = function(main_nr) local name = query.captures[id] lang_capture = determine_language(main_nr, name, node, metadata, lang_capture) + local start_row, start_col, end_row, end_col = node:range() + end_row = end_row - 1 if lang_capture and (name == "content" or name == "injection.content") then -- chunks where the name of the injected language is dynamic -- e.g. markdown code chunks if ts.is_in_node_range(node, row, col) then - return lang_capture, node:range() + return lang_capture, start_row, start_col, end_row, end_col end -- chunks where the name of the language is the name of the capture elseif fn.contains(injectable_languages, name) then if ts.is_in_node_range(node, row, col) then - return name, node:range() + return name, start_row, start_col, end_row, end_col end end end diff --git a/tests/debug-example.sh b/tests/debug-example.sh new file mode 100755 index 0000000..827a73e --- /dev/null +++ b/tests/debug-example.sh @@ -0,0 +1,16 @@ +#! /bin/env bash +# usage: ./tests/debug-example.sh $1 + +echo "choose an example file based on number of extension." +echo "e.g. ./tests/debug-example.sh 01" +echo "e.g. ./tests/debug-example.sh qmd" +echo "Options" +ls ./tests/examples/ + +if [ -z "$1" ]; then + echo "Please provide a file extension or a number" + echo "And call this from the project root directory" + exit 1 +fi + +nvim ./tests/examples/*$1* -c ":lua require'otter'.dev_setup()" diff --git a/tests/examples/01.lua b/tests/examples/00.lua similarity index 100% rename from tests/examples/01.lua rename to tests/examples/00.lua diff --git a/tests/examples/01.qmd b/tests/examples/01.qmd index 35542d4..22b0c98 100644 --- a/tests/examples/01.qmd +++ b/tests/examples/01.qmd @@ -6,6 +6,13 @@ engine: markdown # Hello +```{r} +print('hello world') + +plot(1:10) +``` + + ```{python} print('hello world') ``` diff --git a/tests/examples/03.md b/tests/examples/03.md index 35542d4..c24e709 100644 --- a/tests/examples/03.md +++ b/tests/examples/03.md @@ -6,6 +6,14 @@ engine: markdown # Hello +```lua +print( "this should be formatted") +``` + +```lua +print( "this should be formatted") +``` + ```{python} print('hello world') ``` @@ -43,3 +51,12 @@ def hello(): ```{python} hello() ``` + + +```{python} +import numpy as np + +np.zeros(10) +``` + + diff --git a/tests/examples/05.norg b/tests/examples/05.norg new file mode 100644 index 0000000..f6fbd10 --- /dev/null +++ b/tests/examples/05.norg @@ -0,0 +1,26 @@ +@document.meta +title: Norg Example +description: This is an example of Norg document. +@end + +* Norg Example + +** Code + @code lua + print("Hello, Norg!") + @end + + + @code python + print("Hello world!") + import numpy as np + x = np.zeros(10) + @end + +** Other things + This is a paragraph. + + - This is a list. + - This is another list. + + This is another paragraph. diff --git a/tests/examples/06.ts b/tests/examples/06.ts new file mode 100644 index 0000000..a28b074 --- /dev/null +++ b/tests/examples/06.ts @@ -0,0 +1,16 @@ +const foo = html` +
+

hello

+
+`; + +const bar = css` + .foo { + color: rgb(0, 0, 0); + } +`; + +const lua = lua` + local foo = "bar" + print(foo) +`; From 6b298205844b312588afeb7a88eeabbed3e10f8f Mon Sep 17 00:00:00 2001 From: benlubas Date: Sat, 10 Feb 2024 10:51:54 -0500 Subject: [PATCH 2/7] feat: handle leading whitespace --- README.md | 9 ++-- lua/otter/completion/source.lua | 2 + lua/otter/config.lua | 1 + lua/otter/init.lua | 15 ++++-- lua/otter/keeper.lua | 89 +++++++++++++++++++++++++++++++-- 5 files changed, 104 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4bfe803..afb818e 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ Example in a markdown (or quarto markdown) document `index.md`: ```` # Some markdown - Hello world ```python @@ -39,8 +38,9 @@ We create a hidden buffer for a file `index.md.tmp.py` - import numpy as np - np.zeros(10) +import numpy as np +np.zeros(10) + ```` This contains just the python code and blank lines for all other lines (this keeps line numbers the same, which comes straight from the trick that the quarto dev team uses for the vs code extension as well). @@ -101,6 +101,9 @@ otter.setup{ write_to_disk = false, }, strip_wrapping_quote_characters = { "'", '"', "`" }, + -- Otter may not work the way you expect when entire code blocks are indented (eg. in Org files) + -- When true, otter handles these cases fully. This is a (minor) performance hit + handle_leading_whitespace = false, } ``` diff --git a/lua/otter/completion/source.lua b/lua/otter/completion/source.lua index 4fceefa..80c3328 100644 --- a/lua/otter/completion/source.lua +++ b/lua/otter/completion/source.lua @@ -85,6 +85,8 @@ source.complete = function(self, params, callback) local otter_nrs = self.updater() local win = vim.api.nvim_get_current_win() local lsp_params = vim.lsp.util.make_position_params(win, self.client.offset_encoding) + lsp_params.position.character = lsp_params.position.character + - keeper.get_leading_offset(lsp_params.position.line, self.main_nr) lsp_params.textDocument = { uri = vim.uri_from_bufnr(self.otter_nr), } diff --git a/lua/otter/config.lua b/lua/otter/config.lua index 2264246..df77eee 100644 --- a/lua/otter/config.lua +++ b/lua/otter/config.lua @@ -14,6 +14,7 @@ local default_config = { write_to_disk = false, }, strip_wrapping_quote_characters = { "'", '"', "`" }, + handle_leading_whitespace = false, } M.cfg = default_config diff --git a/lua/otter/init.lua b/lua/otter/init.lua index 9938d73..d388db8 100644 --- a/lua/otter/init.lua +++ b/lua/otter/init.lua @@ -35,7 +35,7 @@ M.dev_setup = function() vim.api.nvim_buf_set_keymap(0, "n", "lf", ":lua require'otter'.ask_format()", { silent = true }) end ---- Activate the current buffer by adding and syncronizing +--- Activate the current buffer by adding and synchronizing --- otter buffers. ---@param languages table ---@param completion boolean|nil @@ -122,7 +122,7 @@ M.activate = function(languages, completion, diagnostics, tsquery) keeper.sync_raft(main_nr) - -- manually attach language server the corresponds to the fileytype + -- manually attach language server the corresponds to the filetype -- without setting the filetype -- to prevent other plugins we don't need in the otter buffers -- from automatically attaching when ft is set @@ -157,9 +157,16 @@ M.activate = function(languages, completion, diagnostics, tsquery) callback = function(_, _) M.sync_raft(main_nr) for bufnr, ns in pairs(nss) do - local diag = vim.diagnostic.get(bufnr) + local diags = vim.diagnostic.get(bufnr) vim.diagnostic.reset(ns, main_nr) - vim.diagnostic.set(ns, main_nr, diag, {}) + if config.cfg.handle_leading_whitespace then + for _, diag in ipairs(diags) do + local offset = keeper.get_leading_offset(diag.lnum, main_nr) + diag.col = diag.col + offset + diag.end_col = diag.end_col + offset + end + end + vim.diagnostic.set(ns, main_nr, diags, {}) end end, }) diff --git a/lua/otter/keeper.lua b/lua/otter/keeper.lua index 0143f21..346076e 100644 --- a/lua/otter/keeper.lua +++ b/lua/otter/keeper.lua @@ -5,6 +5,7 @@ local extensions = require("otter.tools.extensions") local treesitter_iterator = require("otter.tools.treesitter_iterator") local api = vim.api local ts = vim.treesitter +local config = require("otter.config") M._otters_attached = {} @@ -20,7 +21,7 @@ local function determine_language(main_nr, name, node, metadata, current_languag -- e.g. html script tags if injection_language ~= "comment" then -- don't use comment as language, - -- comments with langue insiide are handled in injection.combined + -- comments with language inside are handled in injection.combined return injection_language end elseif metadata["injection.combined"] == true then @@ -38,11 +39,39 @@ local function determine_language(main_nr, name, node, metadata, current_languag end end +---trims the leading whitespace from text +---@param text string +---@param bufnr number host buffer number +---@param starting_ln number +---@return string, number +local function trim_leading_witespace(text, bufnr, starting_ln) + if not config.cfg.handle_leading_whitespace then return text, 0 end + + -- Assume the first line is least indented + -- the first line in the capture doesn't have its leading indent, so we grab from the buffer + local split = vim.split(text, "\n", { trimempty = false }) + if #split == 0 then return text, 0 end + local first_line = vim.api.nvim_buf_get_lines(bufnr, starting_ln, starting_ln + 1, false) + local leading = first_line[1]:match("^%s+") + if not leading then return text, 0 end + for i, line in ipairs(split) do + split[i] = line:gsub("^" .. leading, "") + end + return table.concat(split, "\n"), #leading +end + +---@class CodeChunk +---@field range table +---@field lang string +---@field node any +---@field text string +---@field leading_offset number + ---Extract code chunks from the specified buffer. ---Updates M._otters_attached[main_nr].code_chunks ---@param main_nr integer The main buffer number ---@param lang string|nil language to extract. All languages if nil. ----@return table +---@return CodeChunk[] M.extract_code_chunks = function(main_nr, lang, exclude_eval_false, row_from, row_to) local query = M._otters_attached[main_nr].query local parser = M._otters_attached[main_nr].parser @@ -63,7 +92,7 @@ M.extract_code_chunks = function(main_nr, lang, exclude_eval_false, row_from, ro then -- the actual code content text = ts.get_node_text(node, main_nr, { metadata = metadata[id] }) - -- remove surrounding quotes (workaround for treesitter offets + -- remove surrounding quotes (workaround for treesitter offsets -- not properly processed) text, was_stripped = fn.strip_wrapping_quotes(text) if exclude_eval_false and string.find(text, "| *eval: *false") then @@ -82,11 +111,14 @@ M.extract_code_chunks = function(main_nr, lang, exclude_eval_false, row_from, ro if row_from ~= nil and row_to ~= nil and ((row1 >= row_to and row_to > 0) or row2 < row_from) then goto continue end + local leading_offset + text, leading_offset = trim_leading_witespace(text, main_nr, row1) local result = { range = { from = { row1, col1 }, to = { row2, col2 } }, lang = lang_capture, node = node, text = fn.lines(text), + leading_offset = leading_offset, } if code_chunks[lang_capture] == nil then code_chunks[lang_capture] = {} @@ -104,11 +136,14 @@ M.extract_code_chunks = function(main_nr, lang, exclude_eval_false, row_from, ro -- col1 = col1 + 1 -- col2 = col2 - 1 -- end + local leading_offset + text, leading_offset = trim_leading_witespace(text, main_nr, row1) local result = { range = { from = { row1, col1 }, to = { row2, col2 } }, lang = name, node = node, text = fn.lines(text), + leading_offset = leading_offset, } if code_chunks[name] == nil then code_chunks[name] = {} @@ -158,6 +193,25 @@ M.get_current_language_context = function(main_nr) return nil end +---find the leading_offset of the given line number, and buffer number. Returns 0 if the line number +---isn't in a chunk. +---@param line_nr number +---@param main_nr number +M.get_leading_offset = function(line_nr, main_nr) + if not config.cfg.handle_leading_whitespace then return 0 end + + local lang_chunks = M._otters_attached[main_nr].code_chunks + for _, chunks in pairs(lang_chunks) do + for _, chunk in ipairs(chunks) do + if line_nr >= chunk.range.from[1] and line_nr <= chunk.range.to[1] then + return chunk.leading_offset + end + end + end + return 0 +end + + --- Syncronize the raft of otters attached to a buffer ---@param main_nr integer bufnr of the parent buffer ---@param lang string|nil only sync one otter buffer matching a language @@ -213,13 +267,35 @@ M.sync_raft = function(main_nr, lang) end end +---adjusts IN PLACE the position to include the start and end +---@param obj table +---@param main_nr number +---@param invert boolean? +local function modify_position(obj, main_nr, invert) + if not config.cfg.handle_leading_whitespace then return end + + local sign = invert and 1 or -1 + if obj.range then + local start = obj.range.start + local end_ = obj.range["end"] + local offset = M.get_leading_offset(start.line, main_nr) * sign + obj.range.start.character = start.character + offset + obj.range["end"].character = end_.character + offset + end + + if obj.position then + local pos = obj.position + obj.position.character = pos.character + M.get_leading_offset(pos.line, main_nr) * sign + end +end + --- Send a request to the otter buffers and handle the response. --- The response can optionally be filtered through a function. ---@param main_nr integer bufnr of main buffer ---@param request string lsp request ---@param filter function|nil function to process the response ----@param fallback function|nil optional funtion to call if not in an otter context ----@param handler function|nil optional funtion to handle the filtered lsp request for cases in which the default handler does not suffice +---@param fallback function|nil optional function to call if not in an otter context +---@param handler function|nil optional function to handle the filtered lsp request for cases in which the default handler does not suffice ---@param conf table|nil optional config to pass to the handler. M.send_request = function(main_nr, request, filter, fallback, handler, conf) fallback = fallback or nil @@ -277,6 +353,8 @@ M.send_request = function(main_nr, request, filter, fallback, handler, conf) } end + modify_position(params, main_nr) + vim.lsp.buf_request(otter_nr, request, params, function(err, response, ctx, ...) if response == nil then return @@ -287,6 +365,7 @@ M.send_request = function(main_nr, request, filter, fallback, handler, conf) for _, res in ipairs(response) do local filtered_res = filter(res) if filtered_res then + modify_position(filtered_res, main_nr, true) table.insert(responses, filtered_res) end end From a4f706add24d06b7e461087c8779fd933994ed94 Mon Sep 17 00:00:00 2001 From: benlubas Date: Sat, 10 Feb 2024 11:20:14 -0500 Subject: [PATCH 3/7] fix: rename, backward invert logic --- lua/otter/keeper.lua | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lua/otter/keeper.lua b/lua/otter/keeper.lua index 346076e..649e311 100644 --- a/lua/otter/keeper.lua +++ b/lua/otter/keeper.lua @@ -274,7 +274,7 @@ end local function modify_position(obj, main_nr, invert) if not config.cfg.handle_leading_whitespace then return end - local sign = invert and 1 or -1 + local sign = invert and -1 or 1 if obj.range then local start = obj.range.start local end_ = obj.range["end"] @@ -287,6 +287,14 @@ local function modify_position(obj, main_nr, invert) local pos = obj.position obj.position.character = pos.character + M.get_leading_offset(pos.line, main_nr) * sign end + + if obj.documentChanges then + for _, change in ipairs(obj.documentChanges) do + for _, edit in ipairs(change.edits) do + modify_position(edit, main_nr, invert) + end + end + end end --- Send a request to the otter buffers and handle the response. @@ -353,7 +361,7 @@ M.send_request = function(main_nr, request, filter, fallback, handler, conf) } end - modify_position(params, main_nr) + modify_position(params, main_nr, true) vim.lsp.buf_request(otter_nr, request, params, function(err, response, ctx, ...) if response == nil then @@ -365,7 +373,7 @@ M.send_request = function(main_nr, request, filter, fallback, handler, conf) for _, res in ipairs(response) do local filtered_res = filter(res) if filtered_res then - modify_position(filtered_res, main_nr, true) + modify_position(filtered_res, main_nr) table.insert(responses, filtered_res) end end @@ -373,6 +381,7 @@ M.send_request = function(main_nr, request, filter, fallback, handler, conf) else -- otherwise apply the filter to the one response response = filter(response) + modify_position(response, main_nr) end if response == nil then return From 747e41c54c0d890eda92b26492e3c3ddbc52abd1 Mon Sep 17 00:00:00 2001 From: benlubas Date: Tue, 13 Feb 2024 21:52:08 -0500 Subject: [PATCH 4/7] everything works, but it's a little messy --- lua/otter/keeper.lua | 142 +++++++++++++++++++++++---------- lua/otter/tools/extensions.lua | 22 ++--- tests/examples/05.norg | 35 +++++--- 3 files changed, 137 insertions(+), 62 deletions(-) diff --git a/lua/otter/keeper.lua b/lua/otter/keeper.lua index 649e311..a6029ed 100644 --- a/lua/otter/keeper.lua +++ b/lua/otter/keeper.lua @@ -45,15 +45,21 @@ end ---@param starting_ln number ---@return string, number local function trim_leading_witespace(text, bufnr, starting_ln) - if not config.cfg.handle_leading_whitespace then return text, 0 end + if not config.cfg.handle_leading_whitespace then + return text, 0 + end -- Assume the first line is least indented -- the first line in the capture doesn't have its leading indent, so we grab from the buffer - local split = vim.split(text, "\n", { trimempty = false }) - if #split == 0 then return text, 0 end + local split = vim.split(text, "\n", { trimempty = false }) + if #split == 0 then + return text, 0 + end local first_line = vim.api.nvim_buf_get_lines(bufnr, starting_ln, starting_ln + 1, false) local leading = first_line[1]:match("^%s+") - if not leading then return text, 0 end + if not leading then + return text, 0 + end for i, line in ipairs(split) do split[i] = line:gsub("^" .. leading, "") end @@ -177,17 +183,29 @@ M.get_current_language_context = function(main_nr) local start_row, start_col, end_row, end_col = node:range() end_row = end_row - 1 + local language = nil if lang_capture and (name == "content" or name == "injection.content") then -- chunks where the name of the injected language is dynamic -- e.g. markdown code chunks if ts.is_in_node_range(node, row, col) then - return lang_capture, start_row, start_col, end_row, end_col + language = lang_capture end -- chunks where the name of the language is the name of the capture elseif fn.contains(injectable_languages, name) then if ts.is_in_node_range(node, row, col) then - return name, start_row, start_col, end_row, end_col + language = name + end + end + + if language then + if config.cfg.handle_leading_whitespace then + local buf = M._otters_attached[main_nr].buffers[language] + local lines = vim.api.nvim_buf_get_lines(buf, end_row - 1, end_row, false) + if lines[1] then + end_col = #lines[1] + end end + return language, start_row, start_col, end_row, end_col end end return nil @@ -198,7 +216,9 @@ end ---@param line_nr number ---@param main_nr number M.get_leading_offset = function(line_nr, main_nr) - if not config.cfg.handle_leading_whitespace then return 0 end + if not config.cfg.handle_leading_whitespace then + return 0 + end local lang_chunks = M._otters_attached[main_nr].code_chunks for _, chunks in pairs(lang_chunks) do @@ -211,6 +231,70 @@ M.get_leading_offset = function(line_nr, main_nr) return 0 end +---adjusts IN PLACE the position to include the start and end +---@param obj table +---@param main_nr number +---@param invert boolean? +---@param exclude_end boolean? +---@param known_offset number? +M.modify_position = function(obj, main_nr, invert, exclude_end, known_offset) + if not config.cfg.handle_leading_whitespace or known_offset == 0 then + return + end + + local sign = invert and -1 or 1 + local offset = known_offset + + -- there are apparently a lot of ranges that different language servers can use + local ranges = { "range", "targetSelectionRange", "targetRange", "originSelectionRange" } + for _, range in ipairs(ranges) do + if obj[range] then + local start = obj[range].start + local end_ = obj[range]["end"] + offset = offset or M.get_leading_offset(start.line, main_nr) * sign + obj[range].start.character = start.character + offset + if not exclude_end then + obj[range]["end"].character = end_.character + offset + end + end + end + + if obj.position then + local pos = obj.position + offset = offset or M.get_leading_offset(pos.line, main_nr) * sign + obj.position.character = pos.character + offset + end + + if obj.documentChanges then + for _, change in ipairs(obj.documentChanges) do + if change.edits then + for _, edit in ipairs(change.edits) do + M.modify_position(edit, main_nr, invert, exclude_end, offset) + end + end + end + end + + if obj.changes then + for _, change in pairs(obj.changes) do + for _, edit in ipairs(change) do + M.modify_position(edit, main_nr, invert, exclude_end, offset) + end + end + end + + if obj.newText then + offset = offset or M.get_leading_offset(obj.range.start, main_nr) * sign + local str = "" + for _ = 1, offset, 1 do + str = str .. " " + end + -- Put indents in front of newline, but ignore newlines that are followed by newlines + obj.newText = string.gsub(obj.newText, "(\n)([^\n])", "%1" .. str .. "%2") + obj.newText = string.gsub(obj.newText, "\n$", "\n" .. str) -- match a potential newline at the end + end +end + --- Syncronize the raft of otters attached to a buffer ---@param main_nr integer bufnr of the parent buffer @@ -267,36 +351,6 @@ M.sync_raft = function(main_nr, lang) end end ----adjusts IN PLACE the position to include the start and end ----@param obj table ----@param main_nr number ----@param invert boolean? -local function modify_position(obj, main_nr, invert) - if not config.cfg.handle_leading_whitespace then return end - - local sign = invert and -1 or 1 - if obj.range then - local start = obj.range.start - local end_ = obj.range["end"] - local offset = M.get_leading_offset(start.line, main_nr) * sign - obj.range.start.character = start.character + offset - obj.range["end"].character = end_.character + offset - end - - if obj.position then - local pos = obj.position - obj.position.character = pos.character + M.get_leading_offset(pos.line, main_nr) * sign - end - - if obj.documentChanges then - for _, change in ipairs(obj.documentChanges) do - for _, edit in ipairs(change.edits) do - modify_position(edit, main_nr, invert) - end - end - end -end - --- Send a request to the otter buffers and handle the response. --- The response can optionally be filtered through a function. ---@param main_nr integer bufnr of main buffer @@ -359,9 +413,17 @@ M.send_request = function(main_nr, request, filter, fallback, handler, conf) start = { line = start_row, character = start_col }, ["end"] = { line = end_row, character = end_col }, } + assert(end_row) + local line = vim.api.nvim_buf_get_lines(otter_nr, end_row, end_row + 1, false)[1] + if line then + params.range["end"].character = #line + end + M.modify_position(params, main_nr, true, true) + else + -- formatting gets its own special treatment, everything else gets the same + M.modify_position(params, main_nr, true) end - modify_position(params, main_nr, true) vim.lsp.buf_request(otter_nr, request, params, function(err, response, ctx, ...) if response == nil then @@ -373,7 +435,7 @@ M.send_request = function(main_nr, request, filter, fallback, handler, conf) for _, res in ipairs(response) do local filtered_res = filter(res) if filtered_res then - modify_position(filtered_res, main_nr) + M.modify_position(filtered_res, main_nr) table.insert(responses, filtered_res) end end @@ -381,7 +443,7 @@ M.send_request = function(main_nr, request, filter, fallback, handler, conf) else -- otherwise apply the filter to the one response response = filter(response) - modify_position(response, main_nr) + M.modify_position(response, main_nr) end if response == nil then return diff --git a/lua/otter/tools/extensions.lua b/lua/otter/tools/extensions.lua index 6ca5a5a..e29ca59 100644 --- a/lua/otter/tools/extensions.lua +++ b/lua/otter/tools/extensions.lua @@ -1,16 +1,18 @@ return { - python = "py", - r = "R", - julia = "jl", - lua = "lua", - haskell = "hs", bash = "sh", - html = "html", css = "css", - javascript = "js", - yaml = "yml", - sql = "sql", + dot = "dot", elixir = "ex", + haskell = "hs", + html = "html", + javascript = "js", + julia = "jl", + lua = "lua", markdown = "md", - dot = "dot" + typst = "typ", + python = "py", + r = "R", + rust = "rs", + sql = "sql", + yaml = "yml", } diff --git a/tests/examples/05.norg b/tests/examples/05.norg index f6fbd10..2c41e36 100644 --- a/tests/examples/05.norg +++ b/tests/examples/05.norg @@ -3,24 +3,35 @@ title: Norg Example description: This is an example of Norg document. @end +@code lua +local x = { "this", "is a table" } +print("non-indented code block") +@end * Norg Example + This is an example of an inline highlighted code chunk. It's supported in the next + version of the Neorg TS Parser, but it's not implemented in the current one. I'm + just leaving this here to see how this plugin will handle it in the future. + I can add `print('attached modifier extensions')`(lang:lua) text around the code. + ** Code - @code lua - print("Hello, Norg!") - @end + @code lua + print("Hello, Norg!") + @end - @code python - print("Hello world!") - import numpy as np - x = np.zeros(10) - @end + @code python + print("Hello world!") + import numpy as np + x = np.zeros(10) + @end ** Other things - This is a paragraph. - - This is a list. - - This is another list. + This is a paragraph. + + - This is a list + -- with two indents + ~ and a numbered item - This is another paragraph. + This is another paragraph. From 4ceeb46618f10fc2542362c5d9a9c968001d8406 Mon Sep 17 00:00:00 2001 From: benlubas Date: Sun, 18 Feb 2024 19:36:32 -0500 Subject: [PATCH 5/7] fix: add nil check --- lua/otter/keeper.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lua/otter/keeper.lua b/lua/otter/keeper.lua index a6029ed..818efb1 100644 --- a/lua/otter/keeper.lua +++ b/lua/otter/keeper.lua @@ -200,9 +200,11 @@ M.get_current_language_context = function(main_nr) if language then if config.cfg.handle_leading_whitespace then local buf = M._otters_attached[main_nr].buffers[language] - local lines = vim.api.nvim_buf_get_lines(buf, end_row - 1, end_row, false) - if lines[1] then - end_col = #lines[1] + if buf then + local lines = vim.api.nvim_buf_get_lines(buf, end_row - 1, end_row, false) + if lines[1] then + end_col = #lines[1] + end end end return language, start_row, start_col, end_row, end_col From 501b685c5ce2c272ae65bcf4a3345e40f6346988 Mon Sep 17 00:00:00 2001 From: benlubas Date: Tue, 20 Feb 2024 09:31:41 -0500 Subject: [PATCH 6/7] add more language support and informative error --- lua/otter/init.lua | 16 +++++++++++----- lua/otter/tools/extensions.lua | 9 ++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lua/otter/init.lua b/lua/otter/init.lua index d388db8..bc77bd2 100644 --- a/lua/otter/init.lua +++ b/lua/otter/init.lua @@ -78,6 +78,13 @@ M.activate = function(languages, completion, diagnostics, tsquery) -- create otter buffers for _, lang in ipairs(languages) do + if not extensions[lang] then + vim.notify( + ("[Otter] %s is an unknown language. Please open an issue/PR to get it added"):format(lang), + vim.log.levels.ERROR + ) + goto continue + end local extension = "." .. extensions[lang] if extension ~= nil then local otter_path = path_to_otterpath(main_path, extension) @@ -106,11 +113,9 @@ M.activate = function(languages, completion, diagnostics, tsquery) group = api.nvim_create_augroup("OtterAutowrite" .. otter_nr, {}), callback = function(_, _) if api.nvim_buf_is_loaded(otter_nr) then - api.nvim_buf_call(otter_nr, - function() - vim.cmd("write! " .. otter_path) - end - ) + api.nvim_buf_call(otter_nr, function() + vim.cmd("write! " .. otter_path) + end) end end, }) @@ -118,6 +123,7 @@ M.activate = function(languages, completion, diagnostics, tsquery) api.nvim_buf_set_option(otter_nr, "buftype", "nowrite") end end + ::continue:: end keeper.sync_raft(main_nr) diff --git a/lua/otter/tools/extensions.lua b/lua/otter/tools/extensions.lua index e29ca59..b7a4339 100644 --- a/lua/otter/tools/extensions.lua +++ b/lua/otter/tools/extensions.lua @@ -1,18 +1,25 @@ return { bash = "sh", + c = "c", + cpp = "cpp", css = "css", dot = "dot", elixir = "ex", + go = "go", haskell = "hs", html = "html", javascript = "js", julia = "jl", lua = "lua", markdown = "md", - typst = "typ", + nix = "nix", + php = "php", python = "py", r = "R", + ruby = "rb", rust = "rs", sql = "sql", + typescript = "ts", + typst = "typ", yaml = "yml", } From eef084f721b985922d1e62d8b4a5f105cfc76ef9 Mon Sep 17 00:00:00 2001 From: benlubas Date: Sat, 2 Mar 2024 14:41:11 -0500 Subject: [PATCH 7/7] small things --- lua/otter/keeper.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lua/otter/keeper.lua b/lua/otter/keeper.lua index f5ff345..ec170e0 100644 --- a/lua/otter/keeper.lua +++ b/lua/otter/keeper.lua @@ -87,6 +87,7 @@ M.extract_code_chunks = function(main_nr, lang, exclude_eval_false, row_from, ro local lang_capture = nil for _, match, metadata in query:iter_matches(root, main_nr, 0, -1, { all = true }) do for id, nodes in pairs(match) do + local name = query.captures[id] -- TODO: maybe can be removed with nvim v0.10 if type(nodes) ~= "table" then @@ -94,7 +95,6 @@ M.extract_code_chunks = function(main_nr, lang, exclude_eval_false, row_from, ro end for _, node in ipairs(nodes) do - local name = query.captures[id] local text local was_stripped lang_capture = determine_language(main_nr, name, node, metadata, lang_capture) @@ -168,7 +168,6 @@ M.extract_code_chunks = function(main_nr, lang, exclude_eval_false, row_from, ro end end end - P(code_chunks) return code_chunks end