Skip to content

Commit abf8890

Browse files
authored
feat: support headline's cookie from TODOs (#926)
* update parent cookie from todo * add a test * factor out set_cookie * add update_todo_cookie * tidy up and change var name back * make set_cookie private
1 parent 2c95512 commit abf8890

File tree

2 files changed

+83
-21
lines changed

2 files changed

+83
-21
lines changed

lua/orgmode/files/headline.lua

+61-21
Original file line numberDiff line numberDiff line change
@@ -357,12 +357,14 @@ end
357357
function Headline:set_todo(keyword)
358358
local todo, node = self:get_todo()
359359
if todo then
360-
return self:_set_node_text(node, keyword)
360+
self:_set_node_text(node, keyword)
361+
return self:update_parent_cookie()
361362
end
362363

363364
local stars = self:_get_child_node('stars')
364365
local _, level = stars:end_()
365-
return self:_set_node_text(stars, ('%s %s'):format(('*'):rep(level), keyword))
366+
self:_set_node_text(stars, ('%s %s'):format(('*'):rep(level), keyword))
367+
return self:update_parent_cookie()
366368
end
367369

368370
memoize('get_todo')
@@ -890,36 +892,74 @@ function Headline:get_cookie()
890892
return self:_parse_title_part('%[%d?%d?%d?%%%]')
891893
end
892894

895+
function Headline:_set_cookie(cookie, num, denum)
896+
-- Update the cookie
897+
local new_cookie_val
898+
if self.file:get_node_text(cookie):find('%%') then
899+
new_cookie_val = ('[%d%%]'):format((num / denum) * 100)
900+
else
901+
new_cookie_val = ('[%d/%d]'):format(num, denum)
902+
end
903+
return self:_set_node_text(cookie, new_cookie_val)
904+
end
905+
893906
function Headline:update_cookie()
907+
-- Update cookie state from a check box state change
908+
909+
-- Return early if the headline doesn't have a cookie
910+
local cookie = self:get_cookie()
911+
if not cookie then
912+
return self
913+
end
914+
894915
local section = self:node():parent()
895916
if not section then
896917
return self
897918
end
898919

899-
-- Go through all the lists in this headline and gather checked_boxes
900-
local num_boxes, num_checked_boxes = 0, 0
920+
-- Count checked boxes from all lists
921+
local num_checked_boxes, num_boxes = 0, 0
901922
local body = section:field('body')[1]
902-
for node in body:iter_children() do
903-
if node:type() == 'list' then
904-
local boxes = self:child_checkboxes(node)
905-
num_boxes = num_boxes + #boxes
906-
local checked_boxes = vim.tbl_filter(function(box)
907-
return box:match('%[%w%]')
908-
end, boxes)
909-
num_checked_boxes = num_checked_boxes + #checked_boxes
923+
if body then
924+
for node in body:iter_children() do
925+
if node:type() == 'list' then
926+
local boxes = self:child_checkboxes(node)
927+
num_boxes = num_boxes + #boxes
928+
local checked_boxes = vim.tbl_filter(function(box)
929+
return box:match('%[%w%]')
930+
end, boxes)
931+
num_checked_boxes = num_checked_boxes + #checked_boxes
932+
end
910933
end
911934
end
912935

913-
-- Update the cookie
936+
-- Set the cookie
937+
return self:_set_cookie(cookie, num_checked_boxes, num_boxes)
938+
end
939+
940+
function Headline:update_todo_cookie()
941+
-- Update cookie state from a TODO state change
942+
943+
-- Return early if the headline doesn't have a cookie
914944
local cookie = self:get_cookie()
915-
if cookie then
916-
local new_cookie_val
917-
if self.file:get_node_text(cookie):find('%%') then
918-
new_cookie_val = ('[%d%%]'):format((num_checked_boxes / num_boxes) * 100)
919-
else
920-
new_cookie_val = ('[%d/%d]'):format(num_checked_boxes, num_boxes)
921-
end
922-
return self:_set_node_text(cookie, new_cookie_val)
945+
if not cookie then
946+
return self
947+
end
948+
949+
-- Count done children headlines
950+
local children = self:get_child_headlines()
951+
local dones = vim.tbl_filter(function(h)
952+
return h:is_done()
953+
end, children)
954+
955+
-- Set the cookie
956+
return self:_set_cookie(cookie, #dones, #children)
957+
end
958+
959+
function Headline:update_parent_cookie()
960+
local parent = self:get_parent_headline()
961+
if parent and parent.headline then
962+
parent:update_todo_cookie()
923963
end
924964
return self
925965
end

tests/plenary/ui/mappings/todo_spec.lua

+22
Original file line numberDiff line numberDiff line change
@@ -408,4 +408,26 @@ describe('Todo mappings', function()
408408
' :END:',
409409
}, vim.api.nvim_buf_get_lines(0, 2, 11, false))
410410
end)
411+
412+
it('should update headline cookies when children todo state changes', function()
413+
helpers.create_file({
414+
'* Test orgmode [/]',
415+
'** TODO item',
416+
'** TODO item',
417+
'** TODO item',
418+
'** TODO item',
419+
})
420+
vim.fn.cursor(4, 1)
421+
local now = Date.now()
422+
-- Changing to DONE and adding closed date
423+
vim.cmd([[norm citd]])
424+
assert.are.same({
425+
'* Test orgmode [1/4]',
426+
'** TODO item',
427+
'** TODO item',
428+
'** DONE item',
429+
' CLOSED: [' .. now:to_string() .. ']',
430+
'** TODO item',
431+
}, vim.api.nvim_buf_get_lines(0, 0, 6, false))
432+
end)
411433
end)

0 commit comments

Comments
 (0)