@@ -2,18 +2,20 @@ 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 )
14
15
local this = setmetatable ({
15
16
org_todo_keywords = opts .org_todo_keywords ,
16
17
org_todo_keyword_faces = opts .org_todo_keyword_faces ,
18
+ sequences = {},
17
19
}, self )
18
20
this :_parse ()
19
21
return this
@@ -44,6 +46,19 @@ function TodoKeywords:find(keyword)
44
46
end )
45
47
end
46
48
49
+ --- @param keyword string
50
+ --- @return number | nil sequence index this keyword belongs to
51
+ function TodoKeywords :find_sequence_index (keyword )
52
+ for seq_idx , seq in ipairs (self .sequences ) do
53
+ for _ , todo_keyword in ipairs (seq ) do
54
+ if todo_keyword .value == keyword then
55
+ return seq_idx
56
+ end
57
+ end
58
+ end
59
+ return nil
60
+ end
61
+
47
62
--- @param type OrgTodoKeywordType
48
63
--- @return OrgTodoKeyword
49
64
function TodoKeywords :first_by_type (type )
@@ -60,6 +75,12 @@ function TodoKeywords:all()
60
75
return self .todo_keywords
61
76
end
62
77
78
+ --- @param sequence_idx ? number
79
+ --- @return OrgTodoKeyword[]
80
+ function TodoKeywords :sequence (sequence_idx )
81
+ return self .sequences [sequence_idx or 1 ] or {}
82
+ end
83
+
63
84
--- @return OrgTodoKeyword
64
85
function TodoKeywords :first ()
65
86
return self .todo_keywords [1 ]
@@ -79,29 +100,105 @@ end
79
100
80
101
--- @private
81
102
function TodoKeywords :_parse ()
82
- local todo , done = self :_split_todo_and_done ()
103
+ local first_sequence = self .org_todo_keywords [1 ]
104
+
105
+ if type (first_sequence ) ~= ' table' then
106
+ self :_parse_single_sequence (self .org_todo_keywords )
107
+ return
108
+ end
109
+
110
+ if # self .org_todo_keywords == 1 then
111
+ self :_parse_single_sequence (first_sequence )
112
+ return
113
+ end
114
+
115
+ self :_parse_multiple_sequences (self .org_todo_keywords )
116
+ end
117
+
118
+ --- @private
119
+ --- @param keyword string
120
+ --- @param status_type string ' TODO' or ' DONE'
121
+ --- @param index number
122
+ --- @param seq_idx number
123
+ --- @param used_shortcuts table<string , boolean>
124
+ --- @return OrgTodoKeyword
125
+ function TodoKeywords :_create_keyword (keyword , status_type , index , seq_idx , used_shortcuts )
126
+ local todo_keyword = TodoKeyword :new ({
127
+ type = status_type ,
128
+ keyword = keyword ,
129
+ index = index ,
130
+ sequence_index = seq_idx ,
131
+ })
132
+
133
+ -- Track used shortcuts to avoid conflicts
134
+ if todo_keyword .has_fast_access then
135
+ used_shortcuts [todo_keyword .shortcut ] = true
136
+ elseif not used_shortcuts [todo_keyword .shortcut ] then
137
+ -- Mark it as a fast access key when we have multiple sequences
138
+ if type (self .org_todo_keywords [1 ]) == ' table' and # self .org_todo_keywords > 1 then
139
+ todo_keyword .has_fast_access = true
140
+ used_shortcuts [todo_keyword .shortcut ] = true
141
+ end
142
+ end
143
+
144
+ todo_keyword .hl = self :_get_hl (todo_keyword .value , status_type )
145
+ return todo_keyword
146
+ end
147
+
148
+ --- @private
149
+ --- @param keywords string[]
150
+ --- @param seq_idx number
151
+ --- @param used_shortcuts table<string , boolean>
152
+ --- @param keyword_offset number
153
+ --- @return OrgTodoKeyword[] keywords for the sequence
154
+ --- @return OrgTodoKeyword[] seq_keywords keywords in this sequence
155
+ function TodoKeywords :_parse_sequence (keywords , seq_idx , used_shortcuts , keyword_offset )
156
+ keyword_offset = keyword_offset or 0
157
+ local todo , done = self :_split_todo_and_done (keywords )
83
158
local list = {}
159
+ local seq_keywords = {}
160
+
161
+ -- Process TODO keywords
84
162
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' )
163
+ local todo_keyword = self :_create_keyword (keyword , ' TODO' , keyword_offset + i , seq_idx , used_shortcuts )
91
164
table.insert (list , todo_keyword )
165
+ table.insert (seq_keywords , todo_keyword )
92
166
end
93
167
168
+ -- Process DONE keywords
94
169
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' )
170
+ local todo_keyword = self :_create_keyword (keyword , ' DONE' , keyword_offset + # todo + i , seq_idx , used_shortcuts )
101
171
table.insert (list , todo_keyword )
172
+ table.insert (seq_keywords , todo_keyword )
102
173
end
103
174
104
- self .todo_keywords = list
175
+ return list , seq_keywords
176
+ end
177
+
178
+ --- @param todo_keywords string[]
179
+ function TodoKeywords :_parse_single_sequence (todo_keywords )
180
+ local keywords , seq_keywords = self :_parse_sequence (todo_keywords , 1 , {}, 0 )
181
+ self .todo_keywords = keywords
182
+ self .sequences = { seq_keywords }
183
+ end
184
+
185
+ --- @param todo_keywords string[][]
186
+ --- @private
187
+ function TodoKeywords :_parse_multiple_sequences (todo_keywords )
188
+ self .todo_keywords = {}
189
+ self .sequences = {}
190
+ local used_shortcuts = {}
191
+
192
+ for seq_idx , sequence in ipairs (todo_keywords ) do
193
+ local keyword_offset = # self .todo_keywords
194
+ local keywords , seq_keywords = self :_parse_sequence (sequence , seq_idx , used_shortcuts , keyword_offset )
195
+
196
+ -- Add keywords to the main list and the sequence
197
+ for _ , keyword in ipairs (keywords ) do
198
+ table.insert (self .todo_keywords , keyword )
199
+ end
200
+ table.insert (self .sequences , seq_keywords )
201
+ end
105
202
end
106
203
107
204
--- @private
@@ -116,9 +213,9 @@ function TodoKeywords:_get_hl(keyword, type)
116
213
end
117
214
118
215
--- @private
216
+ --- @param keywords string[]
119
217
--- @return string[] , string[]
120
- function TodoKeywords :_split_todo_and_done ()
121
- local keywords = self .org_todo_keywords
218
+ function TodoKeywords :_split_todo_and_done (keywords )
122
219
local has_separator = vim .tbl_contains (keywords , ' |' )
123
220
if not has_separator then
124
221
return { unpack (keywords , 1 , # keywords - 1 ) }, { keywords [# keywords ] }
0 commit comments