Skip to content

Commit c058767

Browse files
fix(capture): Allow multiple capture buffers (#1031)
1 parent 52dcef1 commit c058767

File tree

8 files changed

+151
-104
lines changed

8 files changed

+151
-104
lines changed

lua/orgmode/api/init.lua

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,12 @@ function OrgApi.refile(opts)
8484

8585
local source_bufnr = utils.get_buffer_by_filename(opts.source.file.filename)
8686
local is_capture = source_bufnr > -1 and vim.b[source_bufnr].org_capture
87-
88-
if is_capture and orgmode.capture._window then
89-
refile_opts.template = orgmode.capture._window.template
87+
if is_capture then
88+
local capture_window = orgmode.capture._windows[vim.b[source_bufnr].org_capture_window_id]
89+
if capture_window then
90+
refile_opts.template = capture_window.template
91+
refile_opts.capture_window = capture_window
92+
end
9093
end
9194

9295
return Promise.resolve()

lua/orgmode/capture/_meta.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
---@class OrgProcessCaptureOpts
1010
---@field template OrgCaptureTemplate
11+
---@field capture_window OrgCaptureWindow
1112
---@field source_file OrgFile
1213
---@field source_headline? OrgHeadline
1314
---@field destination_file OrgFile

lua/orgmode/capture/init.lua

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ local Promise = require('orgmode.utils.promise')
2121
---@field on_pre_refile OrgOnCaptureClose
2222
---@field on_post_refile OrgOnCaptureClose
2323
---@field on_cancel_refile OrgOnCaptureCancel
24-
---@field _window OrgCaptureWindow
24+
---@field private _windows table<number, OrgCaptureWindow>
2525
local Capture = {}
2626
Capture.__index = Capture
2727

@@ -34,6 +34,7 @@ function Capture:new(opts)
3434
this.on_cancel_refile = opts.on_cancel_refile
3535
this.templates = opts.templates or Templates:new()
3636
this.closing_note = this:_setup_closing_note()
37+
this._windows = {}
3738
return this
3839
end
3940

@@ -72,17 +73,18 @@ end
7273
---@param template OrgCaptureTemplate
7374
---@return OrgPromise<OrgCaptureWindow>
7475
function Capture:open_template(template)
75-
self._window = CaptureWindow:new({
76+
local window = CaptureWindow:new({
7677
template = template,
77-
on_open = function()
78+
on_open = function(capture_window)
79+
self._windows[capture_window.id] = capture_window
7880
return self:setup_mappings()
7981
end,
80-
on_close = function()
81-
return self:on_refile_close()
82+
on_close = function(capture_window)
83+
return self:on_refile_close(capture_window)
8284
end,
8385
})
8486

85-
return self._window:open()
87+
return window:open()
8688
end
8789

8890
---@param shortcut string
@@ -94,13 +96,13 @@ function Capture:open_template_by_shortcut(shortcut)
9496
return self:open_template(template)
9597
end
9698

97-
function Capture:on_refile_close()
98-
local is_modified = vim.bo.modified
99-
local opts = self:_get_refile_vars()
99+
---@param capture_window OrgCaptureWindow
100+
function Capture:on_refile_close(capture_window)
101+
local opts = self:_get_refile_vars(capture_window)
100102
if not opts then
101103
return
102104
end
103-
if is_modified then
105+
if capture_window:is_modified() then
104106
local choice =
105107
vim.fn.confirm(string.format('Do you want to refile this to %s?', opts.destination_file.filename), '&Yes\n&No')
106108
vim.cmd([[redraw!]])
@@ -112,15 +114,15 @@ function Capture:on_refile_close()
112114
end
113115
end
114116

115-
vim.defer_fn(function()
117+
vim.schedule(function()
116118
self:_refile_from_capture_buffer(opts)
117-
self._window = nil
118-
end, 0)
119+
end)
119120
end
120121

121122
---Triggered when refiling from capture buffer
122123
function Capture:refile()
123-
local opts = self:_get_refile_vars()
124+
local capture_window = self._windows[vim.b.org_capture_window_id]
125+
local opts = self:_get_refile_vars(capture_window)
124126
if not opts then
125127
return
126128
end
@@ -132,12 +134,14 @@ end
132134
function Capture:refile_to_destination()
133135
local source_file = self.files:get_current_file()
134136
local source_headline = source_file:get_headlines()[1]
137+
local capture_window = self._windows[vim.b.org_capture_window_id]
135138
return self:get_destination():next(function(destination)
136139
if not destination then
137140
return false
138141
end
139142
return self:_refile_from_capture_buffer({
140-
template = self._window.template,
143+
template = capture_window.template,
144+
capture_window = capture_window,
141145
source_file = source_file,
142146
source_headline = source_headline,
143147
destination_file = destination.file,
@@ -203,7 +207,7 @@ function Capture:_refile_from_capture_buffer(opts)
203207
self.on_post_refile(self, opts)
204208
end
205209
utils.echo_info(('Wrote %s'):format(destination_file.filename))
206-
self:kill()
210+
self:kill(false, opts.capture_window.id)
207211
return true
208212
end
209213

@@ -535,22 +539,25 @@ function Capture:build_note_capture(title)
535539
end
536540

537541
---@param from_mapping? boolean
538-
function Capture:kill(from_mapping)
539-
if self._window then
542+
---@param window_id? number
543+
function Capture:kill(from_mapping, window_id)
544+
local window = self._windows[window_id or vim.b.org_capture_window_id]
545+
if window then
540546
if from_mapping and self.on_cancel_refile then
541547
self.on_cancel_refile(self)
542548
end
543-
self._window:kill()
544-
self._window = nil
549+
window:kill()
550+
self._windows[window.id] = nil
545551
end
546552
end
547553

548554
---@private
555+
---@param capture_window OrgCaptureWindow
549556
---@return OrgProcessCaptureOpts | false
550-
function Capture:_get_refile_vars()
551-
local source_file = self.files:get_current_file()
557+
function Capture:_get_refile_vars(capture_window)
558+
local source_file = self.files:get(vim.api.nvim_buf_get_name(capture_window:get_bufnr()))
552559
local source_headline = nil
553-
if not self._window.template.whole_file then
560+
if not capture_window.template.whole_file then
554561
source_headline = source_file:get_headlines()[1]
555562
end
556563

@@ -559,7 +566,8 @@ function Capture:_get_refile_vars()
559566
source_headline = source_headline,
560567
destination_file = nil,
561568
destination_headline = nil,
562-
template = self._window.template,
569+
template = capture_window.template,
570+
capture_window = capture_window,
563571
}
564572

565573
if self.on_pre_refile then

lua/orgmode/capture/window.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
local config = require('orgmode.config')
22
local utils = require('orgmode.utils')
33
local Promise = require('orgmode.utils.promise')
4+
local id_counter = 0
45

56
---@class OrgCaptureWindowOpts
67
---@field template OrgCaptureTemplate
@@ -9,6 +10,7 @@ local Promise = require('orgmode.utils.promise')
910
---@field on_close? fun(self: OrgCaptureWindow)
1011

1112
---@class OrgCaptureWindow :OrgCaptureWindowOpts
13+
---@field id number
1214
---@field private _window fun() | nil
1315
---@field private _bufnr number
1416
local CaptureWindow = {}
@@ -22,6 +24,8 @@ function CaptureWindow:new(opts)
2224
on_finish = opts.on_finish,
2325
on_close = opts.on_close,
2426
}
27+
data.id = id_counter
28+
id_counter = id_counter + 1
2529
return setmetatable(data, CaptureWindow)
2630
end
2731

@@ -38,6 +42,7 @@ function CaptureWindow:open()
3842
vim.api.nvim_buf_set_lines(0, 0, -1, true, content)
3943
self.template:setup()
4044
vim.b.org_capture = true
45+
vim.b.org_capture_window_id = self.id
4146
self._bufnr = vim.api.nvim_get_current_buf()
4247

4348
if self.on_open then
@@ -80,6 +85,16 @@ function CaptureWindow:focus()
8085
return self
8186
end
8287

88+
---@return boolean
89+
function CaptureWindow:is_modified()
90+
return vim.bo[self._bufnr].modified
91+
end
92+
93+
---@return number
94+
function CaptureWindow:get_bufnr()
95+
return self._bufnr
96+
end
97+
8398
function CaptureWindow:kill()
8499
if self._window then
85100
self:_window()

lua/orgmode/ui/menu.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ function Menu:add_option(option)
8686
table.insert(self.items, option)
8787
end
8888

89-
---@param separator OrgMenuSeparator
89+
---@param separator? OrgMenuSeparator
9090
function Menu:add_separator(separator)
9191
self:_validate_separator(separator)
9292
table.insert(self.items, vim.tbl_deep_extend('force', self.separator, separator or {}))

lua/orgmode/utils/init.lua

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ local Promise = require('orgmode.utils.promise')
22
local uv = vim.uv
33
local utils = {}
44
local debounce_timers = {}
5-
local tmp_window_augroup = vim.api.nvim_create_augroup('OrgTmpWindow', { clear = true })
65

76
---@param file string full path to filename
87
---@param opts? { raw: boolean, schedule: boolean } raw: Return raw results, schedule: wrap results in vim.schedule
@@ -415,17 +414,17 @@ function utils.open_tmp_org_window(height, split_mode, border, on_close)
415414
utils.open_window(vim.fn.tempname() .. '.org', height or 16, split_mode, border)
416415
vim.cmd([[setlocal filetype=org bufhidden=wipe nobuflisted nolist noswapfile nofoldenable]])
417416
local bufnr = vim.api.nvim_get_current_buf()
417+
local augroup = vim.api.nvim_create_augroup('OrgTmpWindow_' .. bufnr, { clear = true })
418418

419419
if on_close then
420420
vim.api.nvim_create_autocmd('BufWipeout', {
421-
buffer = 0,
422-
group = tmp_window_augroup,
421+
buffer = bufnr,
422+
group = augroup,
423423
callback = on_close,
424424
once = true,
425425
})
426426
vim.api.nvim_create_autocmd('VimLeavePre', {
427-
buffer = 0,
428-
group = tmp_window_augroup,
427+
group = augroup,
429428
callback = on_close,
430429
once = true,
431430
})
@@ -442,7 +441,7 @@ function utils.open_tmp_org_window(height, split_mode, border, on_close)
442441
end
443442

444443
return function()
445-
vim.api.nvim_create_augroup('OrgTmpWindow', { clear = true })
444+
vim.api.nvim_create_augroup('OrgTmpWindow_' .. bufnr, { clear = true })
446445
close_win()
447446
if prev_winnr and vim.api.nvim_win_is_valid(prev_winnr) then
448447
vim.api.nvim_set_current_win(prev_winnr)

tests/plenary/capture/capture_spec.lua

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
local Capture = require('orgmode.capture')
22
local Templates = require('orgmode.capture.templates')
33
local Template = require('orgmode.capture.template')
4+
local CaptureWindow = require('orgmode.capture.window')
45
local helpers = require('tests.plenary.helpers')
56
local org = require('orgmode')
67

@@ -227,20 +228,23 @@ describe('Capture', function()
227228
local capture_lines = { '* foo' }
228229
local capture_file = helpers.create_file_instance(capture_lines)
229230
local item = capture_file:get_headlines()[1]
231+
local template = Template:new({
232+
properties = {
233+
empty_lines = {
234+
before = 2,
235+
after = 1,
236+
},
237+
},
238+
})
239+
local capture_window = CaptureWindow:new({ template = template })
230240

231241
---@diagnostic disable-next-line: invisible
232242
org.capture:_refile_from_capture_buffer({
233243
destination_file = destination_file,
234244
source_file = capture_file,
235245
source_headline = item,
236-
template = Template:new({
237-
properties = {
238-
empty_lines = {
239-
before = 2,
240-
after = 1,
241-
},
242-
},
243-
}),
246+
template = template,
247+
capture_window = capture_window,
244248
})
245249
vim.cmd('edit ' .. vim.fn.fnameescape(destination_file.filename))
246250
assert.are.same({
@@ -262,6 +266,16 @@ describe('Capture', function()
262266

263267
local capture_lines = { '** baz' }
264268
local capture_file = helpers.create_file(capture_lines)
269+
270+
local template = Template:new({
271+
properties = {
272+
empty_lines = {
273+
before = 2,
274+
after = 1,
275+
},
276+
},
277+
})
278+
local capture_window = CaptureWindow:new({ template = template })
265279
assert(capture_file)
266280
local item = capture_file:get_headlines()[1]
267281

@@ -270,14 +284,8 @@ describe('Capture', function()
270284
destination_file = destination_file,
271285
source_file = capture_file,
272286
source_headline = item,
273-
template = Template:new({
274-
properties = {
275-
empty_lines = {
276-
before = 2,
277-
after = 1,
278-
},
279-
},
280-
}),
287+
template = template,
288+
capture_window = capture_window,
281289
})
282290
vim.cmd('edit ' .. vim.fn.fnameescape(destination_file.filename))
283291
assert.are.same({
@@ -304,21 +312,24 @@ describe('Capture', function()
304312
local capture_lines = { '** baz' }
305313
local capture_file = helpers.create_file_instance(capture_lines)
306314
local item = capture_file:get_headlines()[1]
315+
local template = Template:new({
316+
properties = {
317+
empty_lines = {
318+
before = 2,
319+
after = 1,
320+
},
321+
},
322+
})
323+
local capture_window = CaptureWindow:new({ template = template })
307324

308325
---@diagnostic disable-next-line: invisible
309326
org.capture:_refile_from_capture_buffer({
310327
destination_file = destination_file,
311328
source_file = capture_file,
312329
source_headline = item,
313330
destination_headline = destination_file:get_headlines()[1],
314-
template = Template:new({
315-
properties = {
316-
empty_lines = {
317-
before = 2,
318-
after = 1,
319-
},
320-
},
321-
}),
331+
template = template,
332+
capture_window = capture_window,
322333
})
323334
vim.cmd('edit ' .. vim.fn.fnameescape(destination_file.filename))
324335
assert.are.same({
@@ -346,15 +357,18 @@ describe('Capture', function()
346357
local capture_lines = { '** baz' }
347358
local capture_file = helpers.create_file_instance(capture_lines)
348359
local item = capture_file:get_headlines()[1]
360+
local template = Template:new({
361+
regexp = 'appendhere',
362+
})
363+
local capture_window = CaptureWindow:new({ template = template })
349364

350365
---@diagnostic disable-next-line: invisible
351366
org.capture:_refile_from_capture_buffer({
352367
destination_file = destination_file,
353368
source_file = capture_file,
354369
source_headline = item,
355-
template = Template:new({
356-
regexp = 'appendhere',
357-
}),
370+
template = template,
371+
capture_window = capture_window,
358372
})
359373
vim.cmd('edit ' .. vim.fn.fnameescape(destination_file.filename))
360374
assert.are.same({

0 commit comments

Comments
 (0)