@@ -2,18 +2,26 @@ local utils = require('orgmode.utils')
2
2
local TodoKeyword = require (' orgmode.objects.todo_keywords.todo_keyword' )
3
3
4
4
--- @class OrgTodoKeywords
5
- --- @field org_todo_keywords string[]
5
+ --- @field org_todo_keywords string[][] | string[]
6
6
--- @field org_todo_keyword_faces table<string , string>
7
7
--- @field todo_keywords OrgTodoKeyword[]
8
+ --- @field sequences OrgTodoKeyword[][] Array of todo keyword sequences
8
9
local TodoKeywords = {}
9
10
TodoKeywords .__index = TodoKeywords
10
11
11
- --- @param opts { org_todo_keywords : string[] , org_todo_keyword_faces : table<string , string> }
12
+ --- @param opts { org_todo_keywords : string[][] | string[] , org_todo_keyword_faces : table<string , string> }
12
13
--- @return OrgTodoKeywords
13
14
function TodoKeywords :new (opts )
15
+ -- Normalize input to always be sequences (string[][])
16
+ local normalized_keywords = opts .org_todo_keywords
17
+ if type (normalized_keywords [1 ]) ~= ' table' then
18
+ normalized_keywords = { normalized_keywords }
19
+ end
20
+
14
21
local this = setmetatable ({
15
- org_todo_keywords = opts . org_todo_keywords ,
22
+ org_todo_keywords = normalized_keywords ,
16
23
org_todo_keyword_faces = opts .org_todo_keyword_faces ,
24
+ sequences = {},
17
25
}, self )
18
26
this :_parse ()
19
27
return this
@@ -44,6 +52,19 @@ function TodoKeywords:find(keyword)
44
52
end )
45
53
end
46
54
55
+ --- @param keyword string
56
+ --- @return number | nil sequence index this keyword belongs to
57
+ function TodoKeywords :find_sequence_index (keyword )
58
+ for seq_idx , seq in ipairs (self .sequences ) do
59
+ for _ , todo_keyword in ipairs (seq ) do
60
+ if todo_keyword .value == keyword then
61
+ return seq_idx
62
+ end
63
+ end
64
+ end
65
+ return nil
66
+ end
67
+
47
68
--- @param type OrgTodoKeywordType
48
69
--- @return OrgTodoKeyword
49
70
function TodoKeywords :first_by_type (type )
@@ -60,6 +81,12 @@ function TodoKeywords:all()
60
81
return self .todo_keywords
61
82
end
62
83
84
+ --- @param sequence_idx ? number
85
+ --- @return OrgTodoKeyword[]
86
+ function TodoKeywords :sequence (sequence_idx )
87
+ return self .sequences [sequence_idx or 1 ] or {}
88
+ end
89
+
63
90
--- @return OrgTodoKeyword
64
91
function TodoKeywords :first ()
65
92
return self .todo_keywords [1 ]
79
106
80
107
--- @private
81
108
function TodoKeywords :_parse ()
82
- local todo , done = self :_split_todo_and_done ()
109
+ self .todo_keywords = {}
110
+ self .sequences = {}
111
+ local used_shortcuts = {}
112
+
113
+ for seq_idx , sequence in ipairs (self .org_todo_keywords ) do
114
+ local keyword_offset = # self .todo_keywords
115
+ local keywords , seq_keywords = self :_parse_sequence (sequence , seq_idx , used_shortcuts , keyword_offset )
116
+
117
+ -- Add keywords to the main list and the sequence
118
+ for _ , keyword in ipairs (keywords ) do
119
+ table.insert (self .todo_keywords , keyword )
120
+ end
121
+ table.insert (self .sequences , seq_keywords )
122
+ end
123
+ end
124
+
125
+ --- @private
126
+ --- @param keyword string
127
+ --- @param status_type string ' TODO' or ' DONE'
128
+ --- @param index number
129
+ --- @param seq_idx number
130
+ --- @param used_shortcuts table<string , boolean>
131
+ --- @return OrgTodoKeyword
132
+ function TodoKeywords :_create_keyword (keyword , status_type , index , seq_idx , used_shortcuts )
133
+ local todo_keyword = TodoKeyword :new ({
134
+ type = status_type ,
135
+ keyword = keyword ,
136
+ index = index ,
137
+ sequence_index = seq_idx ,
138
+ })
139
+
140
+ -- Track used shortcuts to avoid conflicts
141
+ if todo_keyword .has_fast_access then
142
+ used_shortcuts [todo_keyword .shortcut ] = true
143
+ elseif not used_shortcuts [todo_keyword .shortcut ] and # self .org_todo_keywords > 1 then
144
+ -- Auto-assign shortcuts when we have multiple sequences
145
+ todo_keyword .has_fast_access = true
146
+ used_shortcuts [todo_keyword .shortcut ] = true
147
+ end
148
+
149
+ todo_keyword .hl = self :_get_hl (todo_keyword .value , status_type )
150
+ return todo_keyword
151
+ end
152
+
153
+ --- @private
154
+ --- @param keywords string[]
155
+ --- @param seq_idx number
156
+ --- @param used_shortcuts table<string , boolean>
157
+ --- @param keyword_offset number
158
+ --- @return OrgTodoKeyword[] keywords for the sequence
159
+ --- @return OrgTodoKeyword[] seq_keywords keywords in this sequence
160
+ function TodoKeywords :_parse_sequence (keywords , seq_idx , used_shortcuts , keyword_offset )
161
+ keyword_offset = keyword_offset or 0
162
+ local todo , done = self :_split_todo_and_done (keywords )
83
163
local list = {}
164
+ local seq_keywords = {}
165
+
166
+ -- Process TODO keywords
84
167
for i , keyword in ipairs (todo ) do
85
- local todo_keyword = TodoKeyword :new ({
86
- type = ' TODO' ,
87
- keyword = keyword ,
88
- index = i ,
89
- })
90
- todo_keyword .hl = self :_get_hl (todo_keyword .value , ' TODO' )
168
+ local todo_keyword = self :_create_keyword (keyword , ' TODO' , keyword_offset + i , seq_idx , used_shortcuts )
91
169
table.insert (list , todo_keyword )
170
+ table.insert (seq_keywords , todo_keyword )
92
171
end
93
172
173
+ -- Process DONE keywords
94
174
for i , keyword in ipairs (done ) do
95
- local todo_keyword = TodoKeyword :new ({
96
- type = ' DONE' ,
97
- keyword = keyword ,
98
- index = # todo + i ,
99
- })
100
- todo_keyword .hl = self :_get_hl (todo_keyword .value , ' DONE' )
175
+ local todo_keyword = self :_create_keyword (keyword , ' DONE' , keyword_offset + # todo + i , seq_idx , used_shortcuts )
101
176
table.insert (list , todo_keyword )
177
+ table.insert (seq_keywords , todo_keyword )
102
178
end
103
179
104
- self . todo_keywords = list
180
+ return list , seq_keywords
105
181
end
106
182
107
183
--- @private
@@ -116,9 +192,9 @@ function TodoKeywords:_get_hl(keyword, type)
116
192
end
117
193
118
194
--- @private
195
+ --- @param keywords string[]
119
196
--- @return string[] , string[]
120
- function TodoKeywords :_split_todo_and_done ()
121
- local keywords = self .org_todo_keywords
197
+ function TodoKeywords :_split_todo_and_done (keywords )
122
198
local has_separator = vim .tbl_contains (keywords , ' |' )
123
199
if not has_separator then
124
200
return { unpack (keywords , 1 , # keywords - 1 ) }, { keywords [# keywords ] }
0 commit comments