From cb0351a7df373b77b0706852fe7d4daae0f1f064 Mon Sep 17 00:00:00 2001 From: Thuyen Ngo Date: Sun, 9 Mar 2025 08:21:32 -0700 Subject: [PATCH 1/6] update parent cookie from todo --- lua/orgmode/files/headline.lua | 68 ++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/lua/orgmode/files/headline.lua b/lua/orgmode/files/headline.lua index 7ca98a59a..c79552d28 100644 --- a/lua/orgmode/files/headline.lua +++ b/lua/orgmode/files/headline.lua @@ -357,12 +357,14 @@ end function Headline:set_todo(keyword) local todo, node = self:get_todo() if todo then - return self:_set_node_text(node, keyword) + self:_set_node_text(node, keyword) + return self:update_parent_cookie() end local stars = self:_get_child_node('stars') local _, level = stars:end_() - return self:_set_node_text(stars, ('%s %s'):format(('*'):rep(level), keyword)) + self:_set_node_text(stars, ('%s %s'):format(('*'):rep(level), keyword)) + return self:update_parent_cookie() end memoize('get_todo') @@ -891,35 +893,53 @@ function Headline:get_cookie() end function Headline:update_cookie() - local section = self:node():parent() - if not section then + -- Return early if the headline doesn't have a cookie + local cookie = self:get_cookie() + if not cookie then return self end - -- Go through all the lists in this headline and gather checked_boxes - local num_boxes, num_checked_boxes = 0, 0 - local body = section:field('body')[1] - for node in body:iter_children() do - if node:type() == 'list' then - local boxes = self:child_checkboxes(node) - num_boxes = num_boxes + #boxes - local checked_boxes = vim.tbl_filter(function(box) - return box:match('%[%w%]') - end, boxes) - num_checked_boxes = num_checked_boxes + #checked_boxes + local num, denum = 0, 0 + -- Count checked boxes from all lists + local section = self:node():parent() + if section then + local body = section:field('body')[1] + if body then + for node in body:iter_children() do + if node:type() == 'list' then + local boxes = self:child_checkboxes(node) + denum = denum + #boxes + local checked_boxes = vim.tbl_filter(function(box) + return box:match('%[%w%]') + end, boxes) + num = num + #checked_boxes + end + end end end + -- Count done children headlines + local children = self:get_child_headlines() + local dones = vim.tbl_filter(function(h) + return h:is_done() + end, children) + num = num + #dones + denum = denum + #children + -- Update the cookie - local cookie = self:get_cookie() - if cookie then - local new_cookie_val - if self.file:get_node_text(cookie):find('%%') then - new_cookie_val = ('[%d%%]'):format((num_checked_boxes / num_boxes) * 100) - else - new_cookie_val = ('[%d/%d]'):format(num_checked_boxes, num_boxes) - end - return self:_set_node_text(cookie, new_cookie_val) + local new_cookie_val + if self.file:get_node_text(cookie):find('%%') then + new_cookie_val = ('[%d%%]'):format((num / denum) * 100) + else + new_cookie_val = ('[%d/%d]'):format(num, denum) + end + return self:_set_node_text(cookie, new_cookie_val) +end + +function Headline:update_parent_cookie() + local parent = self:get_parent_headline() + if parent and parent.headline then + parent:update_cookie() end return self end From ca864f00be5a5a2850fff6e8d7454e5377631059 Mon Sep 17 00:00:00 2001 From: Thuyen Ngo Date: Sun, 9 Mar 2025 10:41:42 -0700 Subject: [PATCH 2/6] add a test --- tests/plenary/ui/mappings/todo_spec.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/plenary/ui/mappings/todo_spec.lua b/tests/plenary/ui/mappings/todo_spec.lua index 2dbcc8190..856a50deb 100644 --- a/tests/plenary/ui/mappings/todo_spec.lua +++ b/tests/plenary/ui/mappings/todo_spec.lua @@ -408,4 +408,26 @@ describe('Todo mappings', function() ' :END:', }, vim.api.nvim_buf_get_lines(0, 2, 11, false)) end) + + it('should update headline cookies when children todo state changes', function() + helpers.create_file({ + '* Test orgmode [/]', + '** TODO item', + '** TODO item', + '** TODO item', + '** TODO item', + }) + vim.fn.cursor(4, 1) + local now = Date.now() + -- Changing to DONE and adding closed date + vim.cmd([[norm citd]]) + assert.are.same({ + '* Test orgmode [1/4]', + '** TODO item', + '** TODO item', + '** DONE item', + ' CLOSED: [' .. now:to_string() .. ']', + '** TODO item', + }, vim.api.nvim_buf_get_lines(0, 0, 6, false)) + end) end) From cb6aa5d3108aa04441380434ee32a3c77414533d Mon Sep 17 00:00:00 2001 From: Thuyen Ngo Date: Sat, 15 Mar 2025 21:06:32 -0700 Subject: [PATCH 3/6] factor out set_cookie --- lua/orgmode/files/headline.lua | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lua/orgmode/files/headline.lua b/lua/orgmode/files/headline.lua index c79552d28..e8cb590e4 100644 --- a/lua/orgmode/files/headline.lua +++ b/lua/orgmode/files/headline.lua @@ -892,6 +892,17 @@ function Headline:get_cookie() return self:_parse_title_part('%[%d?%d?%d?%%%]') end +function Headline:set_cookie(cookie, num, denum) + -- Update the cookie + local new_cookie_val + if self.file:get_node_text(cookie):find('%%') then + new_cookie_val = ('[%d%%]'):format((num / denum) * 100) + else + new_cookie_val = ('[%d/%d]'):format(num, denum) + end + return self:_set_node_text(cookie, new_cookie_val) +end + function Headline:update_cookie() -- Return early if the headline doesn't have a cookie local cookie = self:get_cookie() @@ -927,13 +938,7 @@ function Headline:update_cookie() denum = denum + #children -- Update the cookie - local new_cookie_val - if self.file:get_node_text(cookie):find('%%') then - new_cookie_val = ('[%d%%]'):format((num / denum) * 100) - else - new_cookie_val = ('[%d/%d]'):format(num, denum) - end - return self:_set_node_text(cookie, new_cookie_val) + return self:set_cookie(cookie, num, denum) end function Headline:update_parent_cookie() From f059ec989cec188266d30b1ce313a4ce6d20608a Mon Sep 17 00:00:00 2001 From: Thuyen Ngo Date: Sat, 15 Mar 2025 21:08:36 -0700 Subject: [PATCH 4/6] add update_todo_cookie --- lua/orgmode/files/headline.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lua/orgmode/files/headline.lua b/lua/orgmode/files/headline.lua index e8cb590e4..2efcf5bb1 100644 --- a/lua/orgmode/files/headline.lua +++ b/lua/orgmode/files/headline.lua @@ -929,7 +929,18 @@ function Headline:update_cookie() end end + -- Update the cookie + return self:set_cookie(cookie, num, denum) +end + +function Headline:update_todo_cookie() -- Count done children headlines + -- Return early if the headline doesn't have a cookie + local cookie = self:get_cookie() + if not cookie then + return self + end + local num, denum = 0, 0 local children = self:get_child_headlines() local dones = vim.tbl_filter(function(h) return h:is_done() @@ -944,7 +955,7 @@ end function Headline:update_parent_cookie() local parent = self:get_parent_headline() if parent and parent.headline then - parent:update_cookie() + parent:update_todo_cookie() end return self end From 8124526ba680faadf880558d8a656be8374269d4 Mon Sep 17 00:00:00 2001 From: Thuyen Ngo Date: Sat, 15 Mar 2025 21:20:45 -0700 Subject: [PATCH 5/6] tidy up and change var name back --- lua/orgmode/files/headline.lua | 44 ++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/lua/orgmode/files/headline.lua b/lua/orgmode/files/headline.lua index 2efcf5bb1..456eeab98 100644 --- a/lua/orgmode/files/headline.lua +++ b/lua/orgmode/files/headline.lua @@ -904,52 +904,56 @@ function Headline:set_cookie(cookie, num, denum) end function Headline:update_cookie() + -- Update cookie state from a check box state change + -- Return early if the headline doesn't have a cookie local cookie = self:get_cookie() if not cookie then return self end - local num, denum = 0, 0 - -- Count checked boxes from all lists local section = self:node():parent() - if section then - local body = section:field('body')[1] - if body then - for node in body:iter_children() do - if node:type() == 'list' then - local boxes = self:child_checkboxes(node) - denum = denum + #boxes - local checked_boxes = vim.tbl_filter(function(box) - return box:match('%[%w%]') - end, boxes) - num = num + #checked_boxes - end + if not section then + return self + end + + -- Count checked boxes from all lists + local num_checked_boxes, num_boxes = 0, 0 + local body = section:field('body')[1] + if body then + for node in body:iter_children() do + if node:type() == 'list' then + local boxes = self:child_checkboxes(node) + num_boxes = num_boxes + #boxes + local checked_boxes = vim.tbl_filter(function(box) + return box:match('%[%w%]') + end, boxes) + num_checked_boxes = num_checked_boxes + #checked_boxes end end end -- Update the cookie - return self:set_cookie(cookie, num, denum) + return self:set_cookie(cookie, num_checked_boxes, num_boxes) end function Headline:update_todo_cookie() - -- Count done children headlines + -- Update cookie state from a TODO state change + -- Return early if the headline doesn't have a cookie local cookie = self:get_cookie() if not cookie then return self end - local num, denum = 0, 0 + + -- Count done children headlines local children = self:get_child_headlines() local dones = vim.tbl_filter(function(h) return h:is_done() end, children) - num = num + #dones - denum = denum + #children -- Update the cookie - return self:set_cookie(cookie, num, denum) + return self:set_cookie(cookie, #dones, #children) end function Headline:update_parent_cookie() From 4d8d86afff78b9c43e4f1f2f331bcbc04169d83a Mon Sep 17 00:00:00 2001 From: Thuyen Ngo Date: Sat, 15 Mar 2025 21:36:20 -0700 Subject: [PATCH 6/6] make set_cookie private --- lua/orgmode/files/headline.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lua/orgmode/files/headline.lua b/lua/orgmode/files/headline.lua index 456eeab98..50470098e 100644 --- a/lua/orgmode/files/headline.lua +++ b/lua/orgmode/files/headline.lua @@ -892,7 +892,7 @@ function Headline:get_cookie() return self:_parse_title_part('%[%d?%d?%d?%%%]') end -function Headline:set_cookie(cookie, num, denum) +function Headline:_set_cookie(cookie, num, denum) -- Update the cookie local new_cookie_val if self.file:get_node_text(cookie):find('%%') then @@ -933,8 +933,8 @@ function Headline:update_cookie() end end - -- Update the cookie - return self:set_cookie(cookie, num_checked_boxes, num_boxes) + -- Set the cookie + return self:_set_cookie(cookie, num_checked_boxes, num_boxes) end function Headline:update_todo_cookie() @@ -952,8 +952,8 @@ function Headline:update_todo_cookie() return h:is_done() end, children) - -- Update the cookie - return self:set_cookie(cookie, #dones, #children) + -- Set the cookie + return self:_set_cookie(cookie, #dones, #children) end function Headline:update_parent_cookie()