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`
+
+`;
+
+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