From 53bcd8fba899a9184e529eea032797cbe84dcb97 Mon Sep 17 00:00:00 2001 From: vycdev2 Date: Sat, 3 Oct 2026 04:04:43 +0000 Subject: [PATCH] fix: preserve punctuation when removing completion tags --- CHANGELOG.md | 1 + src/utils/todo-status.ts | 6 +++++- test/todo-status.spec.ts | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ea9f9..fd826b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ #### Review fixes +- Preserved punctuation immediately before a completion or elapsed-time tag when reopening a task. - Preserved balanced closing parentheses, brackets, and braces in relative file-link filenames while excluding surrounding punctuation. - Preserved unsaved archive buffers, serialized simultaneous transfers, and replaced closed archive files only after a complete write. Failed writes, stale source documents, and rejected editor edits keep source tasks intact. - Preserved archive task order, sibling project structure, attached comment indentation, and standalone notes when removing empty projects. diff --git a/src/utils/todo-status.ts b/src/utils/todo-status.ts index aba3bbd..fcd23a7 100644 --- a/src/utils/todo-status.ts +++ b/src/utils/todo-status.ts @@ -59,5 +59,9 @@ export const removeTodoStatusTag = (text: string, pattern: RegExp): string => { if (!match) return text; - return text.slice(0, match.index) + text.slice(match.index + match[0].length); + // The tag-boundary regex includes the character before @. Keep punctuation, + // but consume the separating whitespace as before. + const boundary = match[0][0]; + const prefix = boundary !== '@' && !/\s/.test(boundary) ? boundary : ''; + return text.slice(0, match.index) + prefix + text.slice(match.index + match[0].length); }; diff --git a/test/todo-status.spec.ts b/test/todo-status.spec.ts index 29ff21c..e3c4144 100644 --- a/test/todo-status.spec.ts +++ b/test/todo-status.spec.ts @@ -39,4 +39,13 @@ describe('Todo status matching', () => { ); expect(removeTodoStatusTag('☐ Explain `@done`', tagFinished)).to.equal('☐ Explain `@done`'); }); + + it('preserves punctuation before status tags when unfinishing tasks', () => { + const tagFinished = /(?:^|[^a-zA-Z0-9])@(?:done|cancelled)(?:\([^)]*\))?/; + const tagElapsed = /(?:^|[^a-zA-Z0-9])@(?:lasted|wasted)(?:\([^)]*\))?/; + + expect(removeTodoStatusTag('☐ Ship,@done', tagFinished)).to.equal('☐ Ship,'); + expect(removeTodoStatusTag('☐ Ship!@cancelled(reason)', tagFinished)).to.equal('☐ Ship!'); + expect(removeTodoStatusTag('☐ Ship,@lasted(2h)', tagElapsed)).to.equal('☐ Ship,'); + }); });