Skip to content

Commit 7070d83

Browse files
fix: Adapt headline level to destination headline on refile (#557)
1 parent c5249aa commit 7070d83

File tree

3 files changed

+104
-2
lines changed

3 files changed

+104
-2
lines changed

lua/orgmode/capture/init.lua

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,17 @@ function Capture:refile_to_headline(destination_filename, lines, item, headline_
239239
end
240240
end
241241

242-
if item and item.level <= headline.level then
242+
if item then
243243
-- Refiling in same file just moves the lines from one position
244244
-- to another,so we need to apply demote instantly
245245
local is_same_file = destination_file.filename == item.root.filename
246-
lines = item:demote(headline.level - item.level + 1, true, not is_same_file)
246+
if item.level <= headline.level then
247+
lines = item:demote(headline.level - item.level + 1, true, not is_same_file)
248+
else
249+
lines = item:promote(item.level - headline.level - 1, true, not is_same_file)
250+
end
247251
end
252+
248253
local refiled = self:_refile_to(destination_filename, lines, item, headline.range.end_line)
249254
if not refiled then
250255
return false

lua/orgmode/parser/section.lua

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,53 @@ function Section:demote(amount, demote_child_sections, dryRun)
437437
return lines
438438
end
439439

440+
---@param amount number
441+
---@param promote_child_sections? boolean
442+
---@param dryRun? boolean
443+
---@return string[]
444+
function Section:promote(amount, promote_child_sections, dryRun)
445+
amount = amount or 1
446+
promote_child_sections = promote_child_sections or false
447+
local should_dedent = config.org_indent_mode == 'indent'
448+
local lines = {}
449+
if self.level == 1 then
450+
utils.echo_warning('Cannot demote top level heading.')
451+
return lines
452+
end
453+
local headline_line = self.line:sub(1 + amount)
454+
table.insert(lines, headline_line)
455+
if not dryRun then
456+
vim.api.nvim_call_function('setline', { self.range.start_line, headline_line })
457+
end
458+
if should_dedent then
459+
local contents = self.root:get_node_text_list(self.node)
460+
for i, content in ipairs(contents) do
461+
if i > 1 then
462+
if content:match('^%*+') then
463+
break
464+
end
465+
local can_dedent = vim.trim(content:sub(1, amount)) == ''
466+
local content_line = content
467+
if can_dedent then
468+
content_line = content:sub(1 + amount)
469+
end
470+
table.insert(lines, content_line)
471+
if not dryRun and can_dedent then
472+
vim.api.nvim_call_function('setline', { self.range.start_line + i - 1, content_line })
473+
end
474+
end
475+
end
476+
end
477+
478+
if promote_child_sections then
479+
for _, section in ipairs(self.sections) do
480+
utils.concat(lines, section:promote(amount, true, dryRun))
481+
end
482+
end
483+
484+
return lines
485+
end
486+
440487
---@return boolean
441488
function Section:has_planning()
442489
for _, date in ipairs(self.dates) do

tests/plenary/ui/mappings_spec.lua

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,6 +1701,56 @@ describe('Mappings', function()
17011701
}, vim.api.nvim_buf_get_lines(0, 0, 5, false))
17021702
end)
17031703

1704+
it('should refile to headline and properly demote', function()
1705+
local destination_file = helpers.load_file_content({
1706+
'* foobar',
1707+
'* baz',
1708+
'** foo',
1709+
})
1710+
1711+
local source_file = helpers.load_file_content({
1712+
'* to be refiled',
1713+
'* not to be refiled',
1714+
})
1715+
1716+
source_file = Files.get_current_file()
1717+
local item = source_file:get_closest_headline()
1718+
org.instance().capture:refile_to_headline(destination_file, source_file:get_headline_lines(item), item, 'foobar')
1719+
assert.are.same('* not to be refiled', vim.fn.getline(1))
1720+
vim.cmd('edit' .. vim.fn.fnameescape(destination_file))
1721+
assert.are.same({
1722+
'* foobar',
1723+
'** to be refiled',
1724+
'* baz',
1725+
'** foo',
1726+
}, vim.api.nvim_buf_get_lines(0, 0, 5, false))
1727+
end)
1728+
1729+
it('should refile to headline and properly promote', function()
1730+
local destination_file = helpers.load_file_content({
1731+
'* foobar',
1732+
'* baz',
1733+
'** foo',
1734+
})
1735+
1736+
local source_file = helpers.load_file_content({
1737+
'**** to be refiled',
1738+
'* not to be refiled',
1739+
})
1740+
1741+
source_file = Files.get_current_file()
1742+
local item = source_file:get_closest_headline()
1743+
org.instance().capture:refile_to_headline(destination_file, source_file:get_headline_lines(item), item, 'foobar')
1744+
assert.are.same('* not to be refiled', vim.fn.getline(1))
1745+
vim.cmd('edit' .. vim.fn.fnameescape(destination_file))
1746+
assert.are.same({
1747+
'* foobar',
1748+
'** to be refiled',
1749+
'* baz',
1750+
'** foo',
1751+
}, vim.api.nvim_buf_get_lines(0, 0, 5, false))
1752+
end)
1753+
17041754
it('should update the checklist cookies on a headline', function()
17051755
helpers.load_file_content({
17061756
'* Test orgmode [/]',

0 commit comments

Comments
 (0)