Skip to content

Commit 947850a

Browse files
authored
Increment/Decrement indentation of list items (#779)
* feat: indent listitem - implement (failing) test * feat: implement increase/decrease list item --------- Co-authored-by: Sebastian Flügge <[email protected]>
1 parent c0584ec commit 947850a

File tree

3 files changed

+157
-2
lines changed

3 files changed

+157
-2
lines changed

lua/orgmode/files/elements/listitem.lua

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,42 @@ function Listitem:update_cookie(total_child_checkboxes, checked_child_checkboxes
115115
end
116116
end
117117

118+
---@param line string
119+
---@return string
120+
function Listitem._increase(line)
121+
return ' ' .. line
122+
end
123+
---
124+
---@param line string
125+
---@return string
126+
function Listitem._decrease(line)
127+
local repl, _ = line:gsub('^ ', '', 1)
128+
return repl
129+
end
130+
131+
---@param adjust_fn function
132+
---@param include_childs boolean
133+
function Listitem:_adjust_lines(adjust_fn, include_childs)
134+
local start_row, _, end_row, _ = self.listitem:range()
135+
if not include_childs then
136+
end_row = start_row + 1
137+
end
138+
139+
local lines = vim.api.nvim_buf_get_lines(0, start_row, end_row, false)
140+
for i, line in ipairs(lines) do
141+
lines[i] = adjust_fn(line)
142+
end
143+
vim.api.nvim_buf_set_lines(0, start_row, end_row, false, lines)
144+
end
145+
146+
---@param include_childs boolean
147+
function Listitem:demote(include_childs)
148+
self:_adjust_lines(self._increase, include_childs)
149+
end
150+
151+
---@param include_childs boolean
152+
function Listitem:promote(include_childs)
153+
self:_adjust_lines(self._decrease, include_childs)
154+
end
155+
118156
return Listitem

lua/orgmode/org/mappings.lua

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ local Table = require('orgmode.files.elements.table')
1414
local EventManager = require('orgmode.events')
1515
local events = EventManager.event
1616
local Babel = require('orgmode.babel')
17+
local ListItem = require('orgmode.files.elements.listitem')
1718

1819
---@class OrgMappings
1920
---@field capture OrgCapture
@@ -489,25 +490,58 @@ function OrgMappings:_todo_change_state(direction)
489490
end
490491

491492
function OrgMappings:do_promote(whole_subtree)
493+
local count = vim.v.count1
494+
local win_view = vim.fn.winsaveview() or {}
495+
-- move to the first non-blank character so the current treesitter node is the listitem
496+
vim.cmd([[normal! _]])
497+
498+
local node = ts_utils.get_node_at_cursor()
499+
if node and node:type() == 'bullet' then
500+
local listitem = self.files:get_closest_listitem()
501+
if listitem then
502+
listitem:promote(whole_subtree)
503+
vim.fn.winrestview(win_view)
504+
return
505+
end
506+
end
507+
492508
local headline = self.files:get_closest_headline()
493509
local old_level = headline:get_level()
494510
local foldclosed = vim.fn.foldclosed('.')
495-
headline:promote(vim.v.count1, whole_subtree)
511+
headline:promote(count, whole_subtree)
496512
if foldclosed > -1 and vim.fn.foldclosed('.') == -1 then
497513
vim.cmd([[norm!zc]])
498514
end
499515
EventManager.dispatch(events.HeadlinePromoted:new(self.files:get_closest_headline(), old_level))
516+
vim.fn.winrestview(win_view)
500517
end
501518

502519
function OrgMappings:do_demote(whole_subtree)
520+
local count = vim.v.count1
521+
local win_view = vim.fn.winsaveview() or {}
522+
-- move to the first non-blank character so the current treesitter node is the listitem
523+
vim.cmd([[normal! _]])
524+
525+
local node = ts_utils.get_node_at_cursor()
526+
if node and node:type() == 'bullet' then
527+
local listitem = self.files:get_closest_listitem()
528+
if listitem then
529+
listitem:demote(whole_subtree)
530+
vim.fn.winrestview(win_view)
531+
532+
return
533+
end
534+
end
535+
503536
local headline = self.files:get_closest_headline()
504537
local old_level = headline:get_level()
505538
local foldclosed = vim.fn.foldclosed('.')
506-
headline:demote(vim.v.count1, whole_subtree)
539+
headline:demote(count, whole_subtree)
507540
if foldclosed > -1 and vim.fn.foldclosed('.') == -1 then
508541
vim.cmd([[norm!zc]])
509542
end
510543
EventManager.dispatch(events.HeadlineDemoted:new(self.files:get_closest_headline(), old_level))
544+
vim.fn.winrestview(win_view)
511545
end
512546

513547
function OrgMappings:org_return()
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
local config = require('orgmode.config')
2+
local helpers = require('tests.plenary.helpers')
3+
local org = require('orgmode')
4+
5+
local feed = function(keys, mode)
6+
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(keys, true, true, true), mode, true)
7+
end
8+
9+
describe('with list item', function()
10+
describe('increase indentation', function()
11+
before_each(function()
12+
helpers.create_file({
13+
'* Some headline',
14+
' - content 1',
15+
' - content sub 1',
16+
' - content sub 2',
17+
' - content subsub 1',
18+
' - content subsub 2',
19+
' - content 2',
20+
'',
21+
})
22+
end)
23+
24+
it('with subitems', function()
25+
vim.fn.cursor(2, 1)
26+
vim.cmd([[norm >s]])
27+
assert.are.same('* Some headline', vim.fn.getline(1))
28+
assert.are.same(' - content 1', vim.fn.getline(2))
29+
assert.are.same(' - content sub 1', vim.fn.getline(3))
30+
assert.are.same(' - content sub 2', vim.fn.getline(4))
31+
assert.are.same(' - content subsub 1', vim.fn.getline(5))
32+
assert.are.same(' - content subsub 2', vim.fn.getline(6))
33+
assert.are.same(' - content 2', vim.fn.getline(7))
34+
end)
35+
36+
it('without subitems', function()
37+
vim.fn.cursor(2, 1)
38+
vim.cmd([[norm >>]])
39+
assert.are.same(' - content 1', vim.fn.getline(2))
40+
assert.are.same(' - content sub 1', vim.fn.getline(3))
41+
assert.are.same(' - content sub 2', vim.fn.getline(4))
42+
assert.are.same(' - content subsub 1', vim.fn.getline(5))
43+
assert.are.same(' - content subsub 2', vim.fn.getline(6))
44+
assert.are.same(' - content 2', vim.fn.getline(7))
45+
end)
46+
end)
47+
describe('decrease indentation', function()
48+
before_each(function()
49+
helpers.create_file({
50+
'* Some headline',
51+
' - parent',
52+
' - content 1',
53+
' - content sub 1',
54+
' - content sub 2',
55+
' - content subsub 1',
56+
' - content subsub 2',
57+
' - content 2',
58+
})
59+
end)
60+
it('with subitems', function()
61+
vim.fn.cursor(3, 1)
62+
vim.cmd([[norm <s]])
63+
assert.are.same(' - parent', vim.fn.getline(2))
64+
assert.are.same(' - content 1', vim.fn.getline(3))
65+
assert.are.same(' - content sub 1', vim.fn.getline(4))
66+
assert.are.same(' - content sub 2', vim.fn.getline(5))
67+
assert.are.same(' - content subsub 1', vim.fn.getline(6))
68+
assert.are.same(' - content subsub 2', vim.fn.getline(7))
69+
assert.are.same(' - content 2', vim.fn.getline(8))
70+
end)
71+
it('without subitems', function()
72+
vim.fn.cursor(3, 1)
73+
vim.cmd([[norm <<]])
74+
assert.are.same(' - parent', vim.fn.getline(2))
75+
assert.are.same(' - content 1', vim.fn.getline(3))
76+
assert.are.same(' - content sub 1', vim.fn.getline(4))
77+
assert.are.same(' - content sub 2', vim.fn.getline(5))
78+
assert.are.same(' - content subsub 1', vim.fn.getline(6))
79+
assert.are.same(' - content subsub 2', vim.fn.getline(7))
80+
assert.are.same(' - content 2', vim.fn.getline(8))
81+
end)
82+
end)
83+
end)

0 commit comments

Comments
 (0)