@@ -2,18 +2,20 @@ local utils = require('orgmode.utils')
22local TodoKeyword = require (' orgmode.objects.todo_keywords.todo_keyword' )
33
44--- @class OrgTodoKeywords
5- --- @field org_todo_keywords string[]
5+ --- @field org_todo_keywords string[][] | string[]
66--- @field org_todo_keyword_faces table<string , string>
77--- @field todo_keywords OrgTodoKeyword[]
8+ --- @field sequences OrgTodoKeyword[][] Array of todo keyword sequences
89local TodoKeywords = {}
910TodoKeywords .__index = TodoKeywords
1011
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> }
1213--- @return OrgTodoKeywords
1314function TodoKeywords :new (opts )
1415 local this = setmetatable ({
1516 org_todo_keywords = opts .org_todo_keywords ,
1617 org_todo_keyword_faces = opts .org_todo_keyword_faces ,
18+ sequences = {},
1719 }, self )
1820 this :_parse ()
1921 return this
@@ -44,6 +46,19 @@ function TodoKeywords:find(keyword)
4446 end )
4547end
4648
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+
4762--- @param type OrgTodoKeywordType
4863--- @return OrgTodoKeyword
4964function TodoKeywords :first_by_type (type )
@@ -60,6 +75,12 @@ function TodoKeywords:all()
6075 return self .todo_keywords
6176end
6277
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+
6384--- @return OrgTodoKeyword
6485function TodoKeywords :first ()
6586 return self .todo_keywords [1 ]
@@ -79,29 +100,105 @@ end
79100
80101--- @private
81102function 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 )
83158 local list = {}
159+ local seq_keywords = {}
160+
161+ -- Process TODO keywords
84162 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 )
91164 table.insert (list , todo_keyword )
165+ table.insert (seq_keywords , todo_keyword )
92166 end
93167
168+ -- Process DONE keywords
94169 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 )
101171 table.insert (list , todo_keyword )
172+ table.insert (seq_keywords , todo_keyword )
102173 end
103174
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
105202end
106203
107204--- @private
@@ -116,9 +213,9 @@ function TodoKeywords:_get_hl(keyword, type)
116213end
117214
118215--- @private
216+ --- @param keywords string[]
119217--- @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 )
122219 local has_separator = vim .tbl_contains (keywords , ' |' )
123220 if not has_separator then
124221 return { unpack (keywords , 1 , # keywords - 1 ) }, { keywords [# keywords ] }
0 commit comments