Skip to content

Commit 313ce5a

Browse files
authored
Enable inserting headlines from document root (#503)
1 parent 7ddbdc0 commit 313ce5a

File tree

4 files changed

+151
-10
lines changed

4 files changed

+151
-10
lines changed

lua/orgmode/config/defaults.lua

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,11 @@ local DefaultConfig = {
125125
org_do_demote = '>>',
126126
org_promote_subtree = '<s',
127127
org_demote_subtree = '>s',
128-
org_meta_return = '<Leader><CR>', -- Add heading, item or row
128+
org_meta_return = '<Leader><CR>', -- Add heading, item or row (context-dependent)
129129
org_return = '<CR>',
130-
org_insert_heading_respect_content = '<prefix>ih', -- Add new headling after current heading block with same level
131-
org_insert_todo_heading = '<prefix>iT', -- Add new todo headling right after current heading with same level
132-
org_insert_todo_heading_respect_content = '<prefix>it', -- Add new todo headling after current heading block on same level
130+
org_insert_heading_respect_content = '<prefix>ih', -- Add new heading after current heading block (same level)
131+
org_insert_todo_heading = '<prefix>iT', -- Add new todo heading right after current heading (same level)
132+
org_insert_todo_heading_respect_content = '<prefix>it', -- Add new todo heading after current heading block (same level)
133133
org_move_subtree_up = '<prefix>K',
134134
org_move_subtree_down = '<prefix>J',
135135
org_export = '<prefix>e',

lua/orgmode/org/mappings.lua

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -640,9 +640,13 @@ end
640640
function OrgMappings:insert_heading_respect_content(suffix)
641641
suffix = suffix or ''
642642
local item = Files.get_closest_headline()
643-
local line = config:respect_blank_before_new_entry({ string.rep('*', item.level) .. ' ' .. suffix })
644-
vim.fn.append(item.range.end_line, line)
645-
vim.fn.cursor(item.range.end_line + #line, 0)
643+
if not item then
644+
self:_insert_heading_from_plain_line(suffix)
645+
else
646+
local line = config:respect_blank_before_new_entry({ string.rep('*', item.level) .. ' ' .. suffix })
647+
vim.fn.append(item.range.end_line, line)
648+
vim.fn.cursor(item.range.end_line + #line, 0)
649+
end
646650
return vim.cmd([[startinsert!]])
647651
end
648652

@@ -652,8 +656,41 @@ end
652656

653657
function OrgMappings:insert_todo_heading()
654658
local item = Files.get_closest_headline()
655-
vim.fn.cursor(item.range.start_line, 0)
656-
return self:handle_return(config:get_todo_keywords().TODO[1] .. ' ')
659+
if not item then
660+
self:_insert_heading_from_plain_line(config:get_todo_keywords().TODO[1] .. ' ')
661+
return vim.cmd([[startinsert!]])
662+
else
663+
vim.fn.cursor(item.range.start_line, 0)
664+
return self:handle_return(config:get_todo_keywords().TODO[1] .. ' ')
665+
end
666+
end
667+
668+
function OrgMappings:_insert_heading_from_plain_line(suffix)
669+
suffix = suffix or ''
670+
local linenr = vim.fn.line('.')
671+
local line = vim.fn.getline(linenr)
672+
local heading_prefix = '* ' .. suffix
673+
674+
if #line == 0 then
675+
line = heading_prefix
676+
vim.fn.setline(linenr, line)
677+
vim.fn.cursor(linenr, 0 + #line)
678+
else
679+
if vim.fn.col('.') == 1 then
680+
-- promote whole line to heading
681+
line = heading_prefix .. line
682+
vim.fn.setline(linenr, line)
683+
vim.fn.cursor(linenr, 0 + #line)
684+
else
685+
-- split at cursor
686+
local left = string.sub(line, 0, vim.fn.col('.') - 1)
687+
local right = string.sub(line, vim.fn.col('.'), #line)
688+
line = heading_prefix .. right
689+
vim.fn.setline(linenr, left)
690+
vim.fn.append(linenr, line)
691+
vim.fn.cursor(linenr + 1, 0 + #line)
692+
end
693+
end
657694
end
658695

659696
function OrgMappings:move_subtree_up()

lua/orgmode/parser/files.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ function Files.find_headlines_matching_search_term(term, no_escape, search_extra
221221
end
222222

223223
---@param id? number
224-
---@return Section
224+
---@return Section?
225225
function Files.get_closest_headline(id)
226226
local current_file = Files.get_current_file()
227227
local msg = 'Make sure there are no errors in the document'

tests/plenary/ui/mappings_spec.lua

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,110 @@ describe('Mappings', function()
899899
end
900900
)
901901

902+
it('should insert new todo heading in empty org file', function()
903+
helpers.load_file_content({ '' })
904+
vim.fn.cursor(1, 1)
905+
vim.cmd([[norm ,oiT]])
906+
assert.are.same({ '* TODO ' }, vim.api.nvim_buf_get_lines(0, 0, 2, false))
907+
908+
helpers.load_file_content({ '' })
909+
vim.fn.cursor(1, 1)
910+
vim.cmd([[norm ,oit]])
911+
assert.are.same({ '* TODO ' }, vim.api.nvim_buf_get_lines(0, 0, 2, false))
912+
913+
helpers.load_file_content({ '' })
914+
vim.fn.cursor(1, 1)
915+
vim.cmd([[norm ,oih]])
916+
assert.are.same({ '* ' }, vim.api.nvim_buf_get_lines(0, 0, 2, false))
917+
end)
918+
919+
it('should insert new todo heading on root level', function()
920+
helpers.load_file_content({
921+
'',
922+
'* TODO heading',
923+
})
924+
925+
vim.fn.cursor(1, 1)
926+
vim.cmd([[norm ,oiT]])
927+
assert.are.same({
928+
'* TODO ',
929+
'* TODO heading',
930+
}, vim.api.nvim_buf_get_lines(0, 0, 3, false))
931+
932+
helpers.load_file_content({
933+
'',
934+
'* TODO heading',
935+
})
936+
937+
vim.fn.cursor(1, 1)
938+
vim.cmd([[norm ,oit]])
939+
assert.are.same({
940+
'* TODO ',
941+
'* TODO heading',
942+
}, vim.api.nvim_buf_get_lines(0, 0, 3, false))
943+
944+
helpers.load_file_content({
945+
'',
946+
'* TODO heading',
947+
})
948+
949+
vim.fn.cursor(1, 1)
950+
vim.cmd([[norm ,oih]])
951+
assert.are.same({
952+
'* ',
953+
'* TODO heading',
954+
}, vim.api.nvim_buf_get_lines(0, 0, 3, false))
955+
end)
956+
957+
it('should promote line to (TODO) heading', function()
958+
helpers.load_file_content({ 'foobar' })
959+
vim.fn.cursor(1, 1)
960+
vim.cmd([[norm ,oiT]])
961+
assert.are.same({
962+
'* TODO foobar',
963+
}, vim.api.nvim_buf_get_lines(0, 0, 2, false))
964+
965+
helpers.load_file_content({ 'foobar' })
966+
vim.fn.cursor(1, 1)
967+
vim.cmd([[norm ,oit]])
968+
assert.are.same({
969+
'* TODO foobar',
970+
}, vim.api.nvim_buf_get_lines(0, 0, 2, false))
971+
972+
helpers.load_file_content({ 'foobar' })
973+
vim.fn.cursor(1, 1)
974+
vim.cmd([[norm ,oih]])
975+
assert.are.same({
976+
'* foobar',
977+
}, vim.api.nvim_buf_get_lines(0, 0, 2, false))
978+
end)
979+
980+
it('should promote line left of the cursor to (TODO) heading', function()
981+
helpers.load_file_content({ 'foobar' })
982+
vim.fn.cursor(1, 4)
983+
vim.cmd([[norm ,oiT]])
984+
assert.are.same({
985+
'foo',
986+
'* TODO bar',
987+
}, vim.api.nvim_buf_get_lines(0, 0, 2, false))
988+
989+
helpers.load_file_content({ 'foobar' })
990+
vim.fn.cursor(1, 4)
991+
vim.cmd([[norm ,oit]])
992+
assert.are.same({
993+
'foo',
994+
'* TODO bar',
995+
}, vim.api.nvim_buf_get_lines(0, 0, 2, false))
996+
997+
helpers.load_file_content({ 'foobar' })
998+
vim.fn.cursor(1, 4)
999+
vim.cmd([[norm ,oih]])
1000+
assert.are.same({
1001+
'foo',
1002+
'* bar',
1003+
}, vim.api.nvim_buf_get_lines(0, 0, 2, false))
1004+
end)
1005+
9021006
it('should insert new todo heading after current one (org_insert_todo_heading)', function()
9031007
helpers.load_file_content({
9041008
'#TITLE: Test',

0 commit comments

Comments
 (0)