From 26cd6028b889da23bdcb3a85f44c24ed3ba1d039 Mon Sep 17 00:00:00 2001 From: Quorafind Date: Mon, 1 Sep 2025 20:57:55 +0800 Subject: [PATCH] fix: improve task regex to prevent matching nested brackets in status Updated task regex pattern from [.] to [^\[\]]{1} to ensure only single non-bracket characters are captured as task status. This prevents parsing errors when task text contains square brackets. --- src/common/regex-define.ts | 2 +- .../quick-capture/modals/QuickCaptureModal.ts | 31 ++++++++++++------- .../ui/renderers/MarkdownRenderer.ts | 13 +++++--- .../task-operations/gutter-marker.ts | 24 +++++++++----- 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/common/regex-define.ts b/src/common/regex-define.ts index dc1aa365..8e4ef426 100644 --- a/src/common/regex-define.ts +++ b/src/common/regex-define.ts @@ -1,5 +1,5 @@ // Task identification -const TASK_REGEX = /^(([\s>]*)?(-|\d+\.|\*|\+)\s\[(.)\])\s*(.*)$/m; +const TASK_REGEX = /^(([\s>]*)?(-|\d+\.|\*|\+)\s\[([^\[\]]{1})\])\s+(.*)$/m; // --- Emoji/Tasks Style Regexes --- const EMOJI_START_DATE_REGEX = /🛫\s*(\d{4}-\d{2}-\d{2})/; diff --git a/src/components/features/quick-capture/modals/QuickCaptureModal.ts b/src/components/features/quick-capture/modals/QuickCaptureModal.ts index e46f9ec3..b52797c1 100644 --- a/src/components/features/quick-capture/modals/QuickCaptureModal.ts +++ b/src/components/features/quick-capture/modals/QuickCaptureModal.ts @@ -11,22 +11,31 @@ import { import { createEmbeddableMarkdownEditor, EmbeddableMarkdownEditor, -} from '@/editor-extensions/core/markdown-editor'; -import TaskProgressBarPlugin from '@/index'; -import { saveCapture, processDateTemplates } from '@/utils/file/file-operations'; +} from "@/editor-extensions/core/markdown-editor"; +import TaskProgressBarPlugin from "@/index"; +import { + saveCapture, + processDateTemplates, +} from "@/utils/file/file-operations"; import { FileSuggest } from "@/components/ui/inputs/AutoComplete"; -import { t } from '@/translations/helper'; -import { MarkdownRendererComponent } from '@/components/ui/renderers/MarkdownRenderer'; -import { StatusComponent } from '@/components/ui/feedback/StatusIndicator'; -import { Task } from '@/types/task'; -import { ContextSuggest, ProjectSuggest } from '@/components/ui/inputs/AutoComplete'; +import { t } from "@/translations/helper"; +import { MarkdownRendererComponent } from "@/components/ui/renderers/MarkdownRenderer"; +import { StatusComponent } from "@/components/ui/feedback/StatusIndicator"; +import { Task } from "@/types/task"; +import { + ContextSuggest, + ProjectSuggest, +} from "@/components/ui/inputs/AutoComplete"; import { TimeParsingService, DEFAULT_TIME_PARSING_CONFIG, ParsedTimeResult, LineParseResult, -} from '@/services/time-parsing-service'; -import { SuggestManager, UniversalEditorSuggest } from '@/components/ui/suggest'; +} from "@/services/time-parsing-service"; +import { + SuggestManager, + UniversalEditorSuggest, +} from "@/components/ui/suggest"; interface TaskMetadata { startDate?: Date; @@ -652,7 +661,7 @@ export class QuickCaptureModal extends Modal { // Step 6: Check if line is already a task or a list item const isTaskOrList = cleanedLine .trim() - .match(/^(-|\d+\.|\*|\+)(\s+\[[^\]]+\])?/); + .match(/^(-|\d+\.|\*|\+)(\s+\[[^\]\[]+\])?/); if (isSubTask) { // Don't add metadata to sub-tasks, but still clean time expressions diff --git a/src/components/ui/renderers/MarkdownRenderer.ts b/src/components/ui/renderers/MarkdownRenderer.ts index 18024686..becd68b1 100644 --- a/src/components/ui/renderers/MarkdownRenderer.ts +++ b/src/components/ui/renderers/MarkdownRenderer.ts @@ -76,7 +76,7 @@ export function clearAllMarks(markdown: string): string { // Special handling for tilde prefix dates: remove ~ and 📅 but keep date cleanedMarkdown = cleanedMarkdown.replace(/\s*~\s*📅\s*/g, " "); - + // Remove date fields (symbol followed by date) - normal case symbolsToRemove.forEach((symbol) => { if (!symbol) return; // Should be redundant due to filter, but safe @@ -118,7 +118,7 @@ export function clearAllMarks(markdown: string): string { const escapedOtherSymbols = symbolsToRemove .map((s) => s!.replace(/[.*+?^${}()|[\\\]]/g, "\\$&")) .join(""); - + // Add escaped non-date symbols to lookahead const escapedNonDateSymbols = ["🆔", "⛔", "🏁"] .map((s) => s.replace(/[.*+?^${}()|[\\\]]/g, "\\$&")) @@ -232,7 +232,7 @@ export function clearAllMarks(markdown: string): string { // Remove tags from temporary markdown (where links/code are placeholders) tempMarkdown = removeTagsWithLinkProtection(tempMarkdown); - // Remove context tags from temporary markdown + // Remove context tags from temporary markdown tempMarkdown = tempMarkdown.replace(/@[\w-]+/g, ""); // Remove target location patterns (like "target: office 📁") @@ -240,7 +240,10 @@ export function clearAllMarks(markdown: string): string { tempMarkdown = tempMarkdown.replace(/\s*📁\s*/g, " "); // Remove any remaining simple tags but preserve special tags like #123-123-123 - tempMarkdown = tempMarkdown.replace(/#(?![0-9-]+\b)[^\u2000-\u206F\u2E00-\u2E7F'!"#$%&()*+,.:;<=>?@^`{|}~\[\]\\\s]+/g, ""); + tempMarkdown = tempMarkdown.replace( + /#(?![0-9-]+\b)[^\u2000-\u206F\u2E00-\u2E7F'!"#$%&()*+,.:;<=>?@^`{|}~\[\]\\\s]+/g, + "" + ); // Remove any remaining tilde symbols (~ symbol) that weren't handled by the special case tempMarkdown = tempMarkdown.replace(/\s+~\s+/g, " "); @@ -254,7 +257,7 @@ export function clearAllMarks(markdown: string): string { // Task marker and final cleaning (applied to the string with links/code restored) tempMarkdown = tempMarkdown.replace( - /^([\s>]*)?(-|\d+\.|\*|\+)\s\[(.)\]\s*/, + /^([\s>]*)?(-|\d+\.|\*|\+)\s\[([^\[\]]{1})\]\s*/, "" ); tempMarkdown = tempMarkdown.replace(/^# /, ""); diff --git a/src/editor-extensions/task-operations/gutter-marker.ts b/src/editor-extensions/task-operations/gutter-marker.ts index 59067a78..7bbf8b75 100644 --- a/src/editor-extensions/task-operations/gutter-marker.ts +++ b/src/editor-extensions/task-operations/gutter-marker.ts @@ -18,7 +18,7 @@ import "@/styles/task-gutter.css"; import { getConfig } from "@/common/task-parser-config"; import { TaskParserConfig } from "@/types/TaskParserConfig"; -const taskRegex = /^(([\s>]*)?(-|\d+\.|\*|\+)\s\[(.)\])\s+(.*)$/m; +const taskRegex = /^(([\s>]*)?(-|\d+\.|\*|\+)\s\[([^\[\]]{1})\])\s+(.*)$/m; // Task icon marker class TaskGutterMarker extends GutterMarker { @@ -113,7 +113,7 @@ const showTaskDetails = ( if (plugin.writeAPI) { await plugin.writeAPI.updateTask({ taskId: updatedTask.id, - updates: updatedTask + updates: updatedTask, }); } }; @@ -147,7 +147,10 @@ const getTaskFromLine = ( ): Task | null => { try { // Try to get the task from dataflow index first - if (plugin.dataflowOrchestrator && plugin.settings.projectConfig?.enableEnhancedProject) { + if ( + plugin.dataflowOrchestrator && + plugin.settings.projectConfig?.enableEnhancedProject + ) { try { // Try to find the task by ID in the existing index const taskId = `${filePath}-L${lineNum}`; @@ -164,18 +167,25 @@ const getTaskFromLine = ( // Fallback to direct parser if (!taskParser) { taskParser = new MarkdownTaskParser( - getConfig(plugin.settings.preferMetadataFormat, plugin) as TaskParserConfig + getConfig( + plugin.settings.preferMetadataFormat, + plugin + ) as TaskParserConfig ); } const task = taskParser.parseTask(line, filePath, lineNum); - + // If we have a task and enhanced project is enabled, ensure the ID matches what Dataflow expects - if (task && plugin.dataflowOrchestrator && plugin.settings.projectConfig?.enableEnhancedProject) { + if ( + task && + plugin.dataflowOrchestrator && + plugin.settings.projectConfig?.enableEnhancedProject + ) { // Ensure the task ID matches the format used by Dataflow task.id = `${filePath}-L${lineNum}`; } - + return task; } catch (error) { console.error("Error parsing task:", error);