|
1 | 1 | local Files = require('orgmode.parser.files')
|
2 | 2 | local config = require('orgmode.config')
|
| 3 | +local utils = require('orgmode.utils') |
| 4 | +local ts_utils = require('nvim-treesitter.ts_utils') |
| 5 | + |
| 6 | +local checkbox_query = vim.treesitter.parse_query('org', '(list (listitem (checkbox) @checkbox))') |
| 7 | +local headline_cookie_query = vim.treesitter.parse_query('org', '(headline (cookie) @cookie)') |
3 | 8 |
|
4 | 9 | local function load_code_blocks()
|
5 | 10 | local file = vim.api.nvim_buf_get_name(0)
|
@@ -36,7 +41,115 @@ local function add_todo_keywords_to_spellgood()
|
36 | 41 | end
|
37 | 42 | end
|
38 | 43 |
|
| 44 | +local function _get_cookie_checked_and_total(parent) |
| 45 | + local parent_type = parent:type() |
| 46 | + local start_row, _, end_row, _ = parent:range() |
| 47 | + local checked, total = 0, 0 |
| 48 | + for _, checkbox in checkbox_query:iter_captures(parent, 0, start_row, end_row + 1) do |
| 49 | + local closest_parent = utils.get_closest_parent_of_type(checkbox, parent_type) |
| 50 | + if closest_parent and closest_parent == parent then -- only count direct children |
| 51 | + local checkbox_text = vim.treesitter.get_node_text(checkbox, 0) |
| 52 | + if checkbox_text:match('%[[x|X]%]') then |
| 53 | + checked = checked + 1 |
| 54 | + end |
| 55 | + total = total + 1 |
| 56 | + end |
| 57 | + end |
| 58 | + |
| 59 | + return checked, total |
| 60 | +end |
| 61 | + |
| 62 | +local function _update_checkbox_text(checkbox, checked_children, total_children) |
| 63 | + local checkbox_text |
| 64 | + if total_children == nil then -- if the function is called without child information, we toggle the current value |
| 65 | + checkbox_text = vim.treesitter.get_node_text(checkbox, 0) |
| 66 | + if checkbox_text:match('%[[xX]%]') then |
| 67 | + checkbox_text = '[ ]' |
| 68 | + else |
| 69 | + checkbox_text = '[X]' |
| 70 | + end |
| 71 | + else |
| 72 | + checkbox_text = '[ ]' |
| 73 | + if checked_children == total_children then |
| 74 | + checkbox_text = '[x]' |
| 75 | + elseif checked_children > 0 then |
| 76 | + checkbox_text = '[-]' |
| 77 | + end |
| 78 | + end |
| 79 | + |
| 80 | + utils.update_node_text(checkbox, { checkbox_text }) |
| 81 | +end |
| 82 | + |
| 83 | +local function _update_cookie_text(cookie, checked_children, total_children) |
| 84 | + local cookie_text = vim.treesitter.get_node_text(cookie, 0) |
| 85 | + |
| 86 | + if total_children == nil then |
| 87 | + checked_children, total_children = 0, 0 |
| 88 | + end |
| 89 | + |
| 90 | + local new_cookie |
| 91 | + if cookie_text:find('/') then |
| 92 | + new_cookie = string.format('[%d/%d]', checked_children, total_children) |
| 93 | + else |
| 94 | + if total_children > 0 then |
| 95 | + new_cookie = string.format('[%d%%%%]', (100 * checked_children) / total_children) |
| 96 | + else |
| 97 | + new_cookie = '[0%%%]' |
| 98 | + end |
| 99 | + end |
| 100 | + cookie_text = cookie_text:gsub('%[.*%]', new_cookie) |
| 101 | + utils.update_node_text(cookie, { cookie_text }) |
| 102 | +end |
| 103 | + |
| 104 | +local function update_checkbox(node, checked_children, total_children) |
| 105 | + if not node then |
| 106 | + node = utils.get_closest_parent_of_type(ts_utils.get_node_at_cursor(0), 'listitem') |
| 107 | + if not node then |
| 108 | + return |
| 109 | + end |
| 110 | + end |
| 111 | + |
| 112 | + local checkbox |
| 113 | + local cookie |
| 114 | + for child in node:iter_children() do |
| 115 | + if child:type() == 'checkbox' then |
| 116 | + checkbox = child |
| 117 | + elseif child:type() == 'itemtext' then |
| 118 | + local c_child = child:named_child(0) |
| 119 | + if c_child and c_child:type() == 'cookie' then |
| 120 | + cookie = c_child |
| 121 | + end |
| 122 | + end |
| 123 | + end |
| 124 | + |
| 125 | + if checkbox then |
| 126 | + _update_checkbox_text(checkbox, checked_children, total_children) |
| 127 | + end |
| 128 | + |
| 129 | + if cookie then |
| 130 | + _update_cookie_text(cookie, checked_children, total_children) |
| 131 | + end |
| 132 | + |
| 133 | + local listitem_parent = utils.get_closest_parent_of_type(node:parent(), 'listitem') |
| 134 | + if listitem_parent then |
| 135 | + local list_parent = utils.get_closest_parent_of_type(node, 'list') |
| 136 | + local checked, total = _get_cookie_checked_and_total(list_parent) |
| 137 | + return update_checkbox(listitem_parent, checked, total) |
| 138 | + end |
| 139 | + |
| 140 | + local section = utils.get_closest_parent_of_type(node:parent(), 'section') |
| 141 | + if section then |
| 142 | + local list_parent = utils.get_closest_parent_of_type(node, 'list') |
| 143 | + local checked, total = _get_cookie_checked_and_total(list_parent) |
| 144 | + local start_row, _, end_row, _ = section:range() |
| 145 | + for _, headline_cookie in headline_cookie_query:iter_captures(section, 0, start_row, end_row + 1) do |
| 146 | + _update_cookie_text(headline_cookie, checked, total) |
| 147 | + end |
| 148 | + end |
| 149 | +end |
| 150 | + |
39 | 151 | return {
|
40 | 152 | load_code_blocks = load_code_blocks,
|
41 | 153 | add_todo_keywords_to_spellgood = add_todo_keywords_to_spellgood,
|
| 154 | + update_checkbox = update_checkbox, |
42 | 155 | }
|
0 commit comments