@@ -225,6 +225,7 @@ export async function updateTaskState(
225225 }
226226}
227227
228+ // Prevent concurrent task cycling
228229let taskCycleLock = false ;
229230
230231export async function taskCycleAtPos ( pos : number ) {
@@ -250,56 +251,58 @@ export async function taskCycleAtPos(pos: number) {
250251}
251252
252253export async function taskCycleCommand ( ) {
253- const text = await editor . getText ( ) ;
254- const pos = await editor . getCursor ( ) ;
255- const tree = await markdown . parseMarkdown ( text ) ;
256- addParentPointers ( tree ) ;
254+ if ( taskCycleLock ) return ;
255+ taskCycleLock = true ;
256+ try {
257+ const text = await editor . getText ( ) ;
258+ const pos = await editor . getCursor ( ) ;
259+ const tree = await markdown . parseMarkdown ( text ) ;
260+ addParentPointers ( tree ) ;
257261
258- let node = nodeAtPos ( tree , pos ) ;
259- if ( ! node ) {
260- await editor . flashNotification ( "No task at cursor" ) ;
261- return ;
262- }
263- if ( [ "BulletList" , "Document" ] . includes ( node . type ! ) ) {
264- // Likely at the end of the line, let's back up a position
265- node = nodeAtPos ( tree , pos - 1 ) ;
266- }
267- if ( ! node ) {
268- await editor . flashNotification ( "No task at cursor" ) ;
269- return ;
270- }
271- const taskNode =
272- node . type === "Task"
273- ? node
274- : findParentMatching ( node ! , ( n ) => n . type === "Task" ) ;
275-
276- if ( taskNode ) {
277- const taskState = findNodeOfType ( taskNode ! , "TaskState" ) ;
278- if ( taskState ) {
279- // Cycle states: [ ] -> [x] -> (remove checkbox) -> [ ]
280- await cycleTaskState ( taskState , true ) ;
262+ let node = nodeAtPos ( tree , pos ) ;
263+ if ( ! node ) {
264+ await editor . flashNotification ( "No task at cursor" ) ;
265+ return ;
266+ }
267+ if ( [ "BulletList" , "Document" ] . includes ( node . type ! ) ) {
268+ node = nodeAtPos ( tree , pos - 1 ) ;
269+ }
270+ if ( ! node ) {
271+ await editor . flashNotification ( "No task at cursor" ) ;
272+ return ;
273+ }
274+ const taskNode =
275+ node . type === "Task"
276+ ? node
277+ : findParentMatching ( node ! , ( n ) => n . type === "Task" ) ;
278+
279+ if ( taskNode ) {
280+ const taskState = findNodeOfType ( taskNode ! , "TaskState" ) ;
281+ if ( taskState ) {
282+ await cycleTaskState ( taskState , true ) ;
283+ }
284+ return ;
281285 }
282- return ;
283- }
284286
285- // Convert a bullet point to a task
286- const listItem = findParentMatching ( node ! , ( n ) => n . type === "ListItem" ) ;
287- if ( ! listItem ) {
288- await editor . flashNotification ( "No task at cursor" ) ;
289- return ;
290- }
287+ const listItem = findParentMatching ( node ! , ( n ) => n . type === "ListItem" ) ;
288+ if ( ! listItem ) {
289+ await editor . flashNotification ( "No task at cursor" ) ;
290+ return ;
291+ }
291292
292- // Check if this ListItem already contains a Task (cursor might be at beginning of line)
293- const existingTask = findNodeOfType ( listItem , "Task" ) ;
294- if ( existingTask ) {
295- const taskState = findNodeOfType ( existingTask , "TaskState" ) ;
296- if ( taskState ) {
297- await cycleTaskState ( taskState , true ) ;
293+ const existingTask = findNodeOfType ( listItem , "Task" ) ;
294+ if ( existingTask ) {
295+ const taskState = findNodeOfType ( existingTask , "TaskState" ) ;
296+ if ( taskState ) {
297+ await cycleTaskState ( taskState , true ) ;
298+ }
299+ return ;
298300 }
299- return ;
300- }
301301
302- await convertListItemToTask ( listItem ) ;
302+ await convertListItemToTask ( listItem ) ;
303+ } finally {
304+ taskCycleLock = false ;
305+ }
303306}
304307
305308// Core logic extracted for testability. Mutates tree in place.
0 commit comments