Skip to content

Commit a605d0f

Browse files
committed
Target by Query, refactoring, and bug fixes.
If the target file is already open in a buffer, it will now use that buffer instead of opening a new one and reading into it. This is to prevent errors of the type of "file already open" when trying to write. Did some refactoring just to reduce and reuse code. During that processes I realized how simple it would be to implement the abililty to target a headline (or anywhere else in an norg document I presume) for a user by defining a treesitter query. This is a feature I had thought about and wanted to eventually add, but after the refactor it became a very simple thing to implement so I went ahead.
1 parent a45d252 commit a605d0f

File tree

1 file changed

+91
-61
lines changed

1 file changed

+91
-61
lines changed

lua/neorg/modules/external/capture/module.lua

Lines changed: 91 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ module.private = {
8080
local data = {}
8181

8282
local build_data_entry = function(item)
83-
return { template = item.name, file = item.file, type = item.type, headline = item.headline, path = item.path }
83+
return { template = item.name, file = item.file, type = item.type, headline = item.headline, path = item.path, query = item.query }
8484
end
8585

8686
local item_is_enabled = function(item)
@@ -109,73 +109,102 @@ module.private = {
109109
return false
110110
end
111111

112+
local path = nil
112113
vim.api.nvim_buf_call(data.calling_bufnr, function()
113-
local path = module.required['core.dirman.utils'].expand_path(save_file)
114-
if not path then
115-
vim.notify("Some error", vim.log.levels.ERROR)
116-
return
114+
path = module.required['core.dirman.utils'].expand_path(save_file)
115+
end)
116+
117+
if not path then
118+
vim.notify("Some error", vim.log.levels.ERROR)
119+
return
120+
end
121+
122+
local already_open = vim.fn.bufexists(path) > 0
123+
local target_bufnr = vim.fn.bufnr(path, true)
124+
125+
if not target_bufnr then
126+
vim.notify("Could not open or locate buffer for " .. path, vim.log.levels.ERROR)
127+
return
128+
end
129+
130+
if not already_open then
131+
vim.api.nvim_buf_set_option(target_bufnr, "filetype", "norg")
132+
end
133+
134+
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
135+
if data.type == "entry" then
136+
137+
local ts = module.required['core.integrations.treesitter']
138+
139+
local end_line = function(node)
140+
local child_count = node:child_count()
141+
if child_count > 0 then
142+
local child = node:child(child_count - 1)
143+
return child:end_()
144+
else
145+
return node:end_()
146+
end
117147
end
118148

119-
local save_bufnr = vim.api.nvim_create_buf(false, false)
120-
vim.api.nvim_buf_call(save_bufnr, function()
121-
pcall(vim.cmd.read, path)
122-
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
123-
if data.type == "entry" then
124-
125-
local ts = module.required['core.integrations.treesitter']
126-
127-
local end_line = function(node)
128-
local child_count = node:child_count()
129-
if child_count > 0 then
130-
local child = node:child(child_count - 1)
131-
return child:end_()
132-
else
133-
return node:end_()
134-
end
135-
end
149+
local set_lines_and_write = function(end_linenr)
150+
vim.api.nvim_buf_set_lines(target_bufnr, end_linenr, end_linenr, false, lines)
151+
vim.api.nvim_buf_call(target_bufnr, function()
136152

137-
local cb = function(query, id, node, _)
138-
if(query.captures[id] ~= "org-capture-target") then
139-
return false
140-
end
141-
local end_linenr = end_line(node)
142-
vim.api.nvim_buf_set_lines(0, end_linenr, end_linenr, false, lines)
143-
vim.cmd.write({ args = { path }, bang = true })
144-
return true
145-
end
153+
--Works if the buffer was already open
154+
vim.cmd.norm( { range = { end_linenr + 1, end_linenr + #lines }, args = { "==" } })
146155

147-
if data.headline then
148-
local query = "( (heading1 title: (paragraph_segment) @title_text) (#eq? @title_text " .. data.headline .. ") ) @org-capture-target"
149-
ts.execute_query(query, cb, save_bufnr)
150-
elseif data.path then
151-
local query = "("
152-
local end_parens = ""
153-
154-
local get_headingnr = function(i)
155-
if i > 6 then
156-
return 6
157-
else
158-
return i
159-
end
160-
end
161-
162-
for i, headline in ipairs(data.path) do
163-
local headingnr = get_headingnr(i)
164-
query = query .. "(heading" .. headingnr .. " title: (paragraph_segment) @t" .. i .. " (#eq? @t" .. i .. " \"" .. headline .. "\"" .. ") "
165-
end_parens = end_parens .. ")"
166-
end
167-
168-
query = query .. " ) @org-capture-target" .. end_parens
169-
170-
ts.execute_query(query, cb, save_bufnr)
171-
else
172-
vim.api.nvim_buf_set_lines(0, -1, -1, false, lines)
173-
vim.cmd.write({ args = { path }, bang = true })
174-
end
156+
vim.cmd.write({ args = { path }, bang = true })
157+
end)
158+
end
175159

160+
local cb = function(query, id, node, _)
161+
if(query.captures[id] ~= "org-capture-target") then
162+
return false
176163
end
177-
end)
178-
end)
164+
local end_linenr = end_line(node)
165+
set_lines_and_write(end_linenr)
166+
return true -- Returning true makes `ts.execute_query` stop iterating over captures
167+
end
168+
169+
local get_headingnr = function(i)
170+
if i > 6 then
171+
return 6
172+
else
173+
return i
174+
end
175+
end
176+
177+
local build_query = function(headline_path)
178+
local query = "("
179+
local end_parens = ""
180+
for i, headline in ipairs(headline_path) do
181+
local headingnr = get_headingnr(i)
182+
query = query .. "(heading" .. headingnr .. " title: (paragraph_segment) @t" .. i .. " (#eq? @t" .. i .. " \"" .. headline .. "\"" .. ") "
183+
end_parens = end_parens .. ")"
184+
end
185+
query = query .. " ) @org-capture-target" .. end_parens
186+
return query
187+
end
188+
189+
local exec_query = function(query)
190+
ts.execute_query(query, cb, target_bufnr)
191+
end
192+
193+
local build_and_execute_query = function(path_or_headline)
194+
local query = build_query(path_or_headline)
195+
exec_query(query)
196+
end
197+
198+
if data.headline then
199+
build_and_execute_query({ data.headline })
200+
elseif data.path then
201+
build_and_execute_query(data.path)
202+
elseif data.query then
203+
exec_query(data.query)
204+
else
205+
set_lines_and_write(-1) -- Negative one means the end of the file
206+
end
207+
end
179208

180209
end
181210
}
@@ -203,6 +232,7 @@ module.on_event = function(event)
203232
type = data[idx].type,
204233
headline = data[idx].headline,
205234
path = data[idx].path,
235+
query = data[idx].query,
206236
calling_file = current_name,
207237
calling_bufnr = calling_bufnr,
208238
on_save = function(bufnr, passed_data) module.private.on_save(bufnr, passed_data) end

0 commit comments

Comments
 (0)