Skip to content

Commit dc28ebb

Browse files
Merge pull request #198 from jgollenz/feature/toggle-heading
2 parents 23526f9 + acf7034 commit dc28ebb

File tree

6 files changed

+83
-0
lines changed

6 files changed

+83
-0
lines changed

DOCS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,9 @@ Cycle todo keyword backward on current headline.
637637
#### **org_toggle_checkbox**
638638
*mapped to*: `<C-Space>`<br />
639639
Toggle current line checkbox state
640+
#### **org_toggle_heading**
641+
*mapped to*: `<Leader>o*`<br />
642+
Toggle current line to headline and vice versa. Checkboxes will turn into TODO headlines.
640643
#### **org_open_at_point**
641644
*mapped to*: `<Leader>oo`<br />
642645
Open hyperlink or date under cursor.<br />

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ set concealcursor=nc
295295
* Change headline TODO state: forward<kbd>cit</kbd> or backward<kbd>ciT</kbd>
296296
* Open hyperlink or date under cursor: <kbd>\<Leader\>oo</kbd>
297297
* Toggle checkbox: <kbd>\<C-space\></kbd>
298+
* Toggle current line to headline and vice versa: <kbd>\<Leader\>o*</kbd>
298299
* Toggle folding of current headline: <kbd>\<TAB\></kbd>
299300
* Toggle folding in whole file: <kbd>\<S-TAB\></kbd>
300301
* Archive headline: <kbd>\<Leader\>o$</kbd>

lua/orgmode/config/defaults.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ return {
102102
org_todo = 'cit',
103103
org_todo_prev = 'ciT',
104104
org_toggle_checkbox = '<C-Space>',
105+
org_toggle_heading = '<Leader>o*',
105106
org_open_at_point = '<Leader>oo',
106107
org_edit_special = [[<Leader>o']],
107108
org_cycle = '<TAB>',

lua/orgmode/config/mappings.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ return {
4848
org_priority_down = { 'org_mappings.priority_down' },
4949
org_todo_prev = { 'org_mappings.todo_prev_state' },
5050
org_toggle_checkbox = { 'org_mappings.toggle_checkbox' },
51+
org_toggle_heading = { 'org_mappings.toggle_heading' },
5152
org_open_at_point = { 'org_mappings.open_at_point' },
5253
org_edit_special = { 'org_mappings.edit_special' },
5354
org_cycle = { 'org_mappings.cycle' },

lua/orgmode/org/mappings.lua

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,36 @@ function OrgMappings:todo_prev_state()
296296
self:_todo_change_state('prev')
297297
end
298298

299+
function OrgMappings:toggle_heading()
300+
local line = vim.fn.getline('.')
301+
local parent = Files.get_closest_headline()
302+
if not parent then
303+
line = '* ' .. line
304+
vim.fn.setline('.', line)
305+
return
306+
end
307+
308+
if parent.line_number == vim.api.nvim_win_get_cursor(0)[1] then
309+
line = line:gsub('^%*+%s', '')
310+
else
311+
line = line:gsub('^(%s*)', '')
312+
if line:match('^[%*-]%s') then -- handle lists
313+
line = line:gsub('^[%*-]%s', '') -- strip bullet
314+
line = line:gsub('^%[([X%s])%]%s', function(checkbox_state)
315+
if checkbox_state == 'X' then
316+
return config:get_todo_keywords().DONE[1] .. ' '
317+
else
318+
return config:get_todo_keywords().TODO[1] .. ' '
319+
end
320+
end)
321+
end
322+
323+
line = string.rep('*', parent.level + 1) .. ' ' .. line
324+
end
325+
326+
vim.fn.setline('.', line)
327+
end
328+
299329
function OrgMappings:_todo_change_state(direction)
300330
local item = Files.get_closest_headline()
301331
local was_done = item:is_done()

tests/plenary/ui/mappings_spec.lua

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,53 @@ describe('Mappings', function()
322322
assert.are.same(' - [ ] The checkbox 2', vim.fn.getline(3))
323323
end)
324324

325+
it('should toggle the current line into a headline and vice versa', function()
326+
helpers.load_file_content({
327+
'top level line',
328+
'* top level heading',
329+
' simple line',
330+
' - list item',
331+
' * [ ] unfinished checkbox item',
332+
' - [X] finished checkbox item',
333+
})
334+
335+
assert.are.same('top level line', vim.fn.getline(1))
336+
assert.are.same(' simple line', vim.fn.getline(3))
337+
assert.are.same(' - list item', vim.fn.getline(4))
338+
assert.are.same(' * [ ] unfinished checkbox item', vim.fn.getline(5))
339+
assert.are.same(' - [X] finished checkbox item', vim.fn.getline(6))
340+
341+
vim.fn.cursor(1, 1)
342+
vim.cmd([[norm ,o*]])
343+
assert.are.same('* top level line', vim.fn.getline(1))
344+
vim.cmd([[norm ,o*]])
345+
assert.are.same('top level line', vim.fn.getline(1))
346+
347+
vim.fn.cursor(3, 1)
348+
vim.cmd([[norm ,o*]])
349+
assert.are.same('** simple line', vim.fn.getline(3))
350+
vim.cmd([[norm ,o*]])
351+
assert.are.same('simple line', vim.fn.getline(3))
352+
353+
vim.fn.cursor(4, 1)
354+
vim.cmd([[norm ,o*]])
355+
assert.are.same('** list item', vim.fn.getline(4))
356+
vim.cmd([[norm ,o*]])
357+
assert.are.same('list item', vim.fn.getline(4))
358+
359+
vim.fn.cursor(5, 1)
360+
vim.cmd([[norm ,o*]])
361+
assert.are.same('** TODO unfinished checkbox item', vim.fn.getline(5))
362+
vim.cmd([[norm ,o*]])
363+
assert.are.same('TODO unfinished checkbox item', vim.fn.getline(5))
364+
365+
vim.fn.cursor(6, 1)
366+
vim.cmd([[norm ,o*]])
367+
assert.are.same('** DONE finished checkbox item', vim.fn.getline(6))
368+
vim.cmd([[norm ,o*]])
369+
assert.are.same('DONE finished checkbox item', vim.fn.getline(6))
370+
end)
371+
325372
it('should toggle archive tag on headline (org_toggle_archive_tag)', function()
326373
helpers.load_file_content({
327374
'#TITLE: Test',

0 commit comments

Comments
 (0)