diff --git a/src/settings.ts b/src/settings.ts index 86bae1c..65c930c 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -4,6 +4,8 @@ interface ExtendedTaskListsSettings { todoFilename: string; excludeFilePattern: string; excludeFolderFilename: string; + excludeRegionBegin: string; + excludeRegionEnd: string; useFullFilepath: boolean; includeNotStarted: boolean; includeInProgress: boolean; @@ -15,6 +17,8 @@ const DEFAULT_SETTINGS: ExtendedTaskListsSettings = { todoFilename: "TODO.md", excludeFilePattern: "", excludeFolderFilename: ".exclude_todos", + excludeRegionBegin: "%% exclude: start %%", + excludeRegionEnd: "%% exclude: end %%", useFullFilepath: false, includeNotStarted: true, includeInProgress: true, @@ -95,6 +99,34 @@ class ExtendedTaskListsSettingTab extends PluginSettingTab { }), ); + new Setting(containerEl) + .setName("Exclude region begin") + .setDesc( + "A line matching this pattern marks the start of a region whose task items are excluded from the generated TODO file.", + ) + .addText((text) => + text + .setValue(this.plugin.settings.excludeRegionBegin) + .onChange(async (value) => { + this.plugin.settings.excludeRegionBegin = value; + await this.plugin.saveSettings(); + }), + ); + + new Setting(containerEl) + .setName("Exclude region end") + .setDesc( + "A line matching this pattern marks the end of an excluded region.", + ) + .addText((text) => + text + .setValue(this.plugin.settings.excludeRegionEnd) + .onChange(async (value) => { + this.plugin.settings.excludeRegionEnd = value; + await this.plugin.saveSettings(); + }), + ); + new Setting(containerEl).setName("Includes").setHeading(); new Setting(containerEl) diff --git a/src/todoService.test.ts b/src/todoService.test.ts index 289cd45..c1d6d32 100644 --- a/src/todoService.test.ts +++ b/src/todoService.test.ts @@ -7,6 +7,8 @@ const MOCK_SETTINGS = { todoFilename: "TODO.md", excludeFilePattern: "", excludeFolderFilename: ".exclude_todos", + excludeRegionBegin: "%% exclude: start %%", + excludeRegionEnd: "%% exclude: end %%", useFullFilepath: false, includeNotStarted: true, includeInProgress: true, @@ -380,6 +382,76 @@ describe("TodoService", () => { expect(actual).toEqual(expected); }); + test("parseTodos excludes task items inside exclude regions", () => { + // Arrange + const content = `- [ ] Included +%% exclude: start %% +- [ ] Excluded +- [.] Also excluded +%% exclude: end %% +- [ ] Also included`; + + const mockFileService = new MockFileService([]); + + // Act + const todoService = new TodoService(mockFileService, MOCK_SETTINGS); + const actual = todoService.parseTodos(content); + + // Assert + expect(actual).toEqual([ + { + task: TaskType.NotStarted, + text: "Included", + indentation: "", + lineno: 0, + } as Todo, + { + task: TaskType.NotStarted, + text: "Also included", + indentation: "", + lineno: 5, + } as Todo, + ]); + }); + + test("parseTodos handles multiple exclude regions", () => { + // Arrange + const content = `- [ ] First +%% exclude: start %% +- [ ] Excluded 1 +%% exclude: end %% +- [ ] Second +%% exclude: start %% +- [ ] Excluded 2 +%% exclude: end %% +- [ ] Third`; + + const mockFileService = new MockFileService([]); + + // Act + const todoService = new TodoService(mockFileService, MOCK_SETTINGS); + const actual = todoService.parseTodos(content); + + // Assert + expect(actual.map((t) => t.text)).toEqual(["First", "Second", "Third"]); + }); + + test("parseTodos excludes to end of file when region is not closed", () => { + // Arrange + const content = `- [ ] Included +%% exclude: start %% +- [ ] Excluded`; + + const mockFileService = new MockFileService([]); + + // Act + const todoService = new TodoService(mockFileService, MOCK_SETTINGS); + const actual = todoService.parseTodos(content); + + // Assert + expect(actual.map((t) => t.text)).toEqual(["Included"]); + }); + test("Whole shebang formats TODO.md correctly with task items nested under normal lists", async () => { // Arrange const taskFile = createMockFile( diff --git a/src/todoService.ts b/src/todoService.ts index b4dccbe..beb3b93 100644 --- a/src/todoService.ts +++ b/src/todoService.ts @@ -114,8 +114,23 @@ class TodoService { */ parseTodos(contents: string): Todo[] { const lines = contents.split(/[\r]?[\n]/); + const beginPattern = this.settings.excludeRegionBegin; + const endPattern = this.settings.excludeRegionEnd; + let inExcludedRegion = false; const matchesAndIndices = lines .map((line, index) => { + if (beginPattern && line.trim() === beginPattern) { + inExcludedRegion = true; + } + if (inExcludedRegion) { + if (endPattern && line.trim() === endPattern) { + inExcludedRegion = false; + } + return { + match: null as unknown as RegExpMatchArray, + index, + } as IndexMatch; + } const match = line.match(TODO_PATTERN); return { match, index } as IndexMatch; })