Skip to content

Commit b85ba54

Browse files
authored
Merge pull request #36 from joeriddles/19-exclude-regions
feat: exclude regions
2 parents 6b62d64 + 383ec89 commit b85ba54

3 files changed

Lines changed: 119 additions & 0 deletions

File tree

src/settings.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ interface ExtendedTaskListsSettings {
44
todoFilename: string;
55
excludeFilePattern: string;
66
excludeFolderFilename: string;
7+
excludeRegionBegin: string;
8+
excludeRegionEnd: string;
79
useFullFilepath: boolean;
810
includeNotStarted: boolean;
911
includeInProgress: boolean;
@@ -15,6 +17,8 @@ const DEFAULT_SETTINGS: ExtendedTaskListsSettings = {
1517
todoFilename: "TODO.md",
1618
excludeFilePattern: "<!-- exclude TODO -->",
1719
excludeFolderFilename: ".exclude_todos",
20+
excludeRegionBegin: "%% exclude: start %%",
21+
excludeRegionEnd: "%% exclude: end %%",
1822
useFullFilepath: false,
1923
includeNotStarted: true,
2024
includeInProgress: true,
@@ -95,6 +99,34 @@ class ExtendedTaskListsSettingTab extends PluginSettingTab {
9599
}),
96100
);
97101

102+
new Setting(containerEl)
103+
.setName("Exclude region begin")
104+
.setDesc(
105+
"A line matching this pattern marks the start of a region whose task items are excluded from the generated TODO file.",
106+
)
107+
.addText((text) =>
108+
text
109+
.setValue(this.plugin.settings.excludeRegionBegin)
110+
.onChange(async (value) => {
111+
this.plugin.settings.excludeRegionBegin = value;
112+
await this.plugin.saveSettings();
113+
}),
114+
);
115+
116+
new Setting(containerEl)
117+
.setName("Exclude region end")
118+
.setDesc(
119+
"A line matching this pattern marks the end of an excluded region.",
120+
)
121+
.addText((text) =>
122+
text
123+
.setValue(this.plugin.settings.excludeRegionEnd)
124+
.onChange(async (value) => {
125+
this.plugin.settings.excludeRegionEnd = value;
126+
await this.plugin.saveSettings();
127+
}),
128+
);
129+
98130
new Setting(containerEl).setName("Includes").setHeading();
99131

100132
new Setting(containerEl)

src/todoService.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const MOCK_SETTINGS = {
77
todoFilename: "TODO.md",
88
excludeFilePattern: "<!-- exclude TODO -->",
99
excludeFolderFilename: ".exclude_todos",
10+
excludeRegionBegin: "%% exclude: start %%",
11+
excludeRegionEnd: "%% exclude: end %%",
1012
useFullFilepath: false,
1113
includeNotStarted: true,
1214
includeInProgress: true,
@@ -380,6 +382,76 @@ describe("TodoService", () => {
380382
expect(actual).toEqual(expected);
381383
});
382384

385+
test("parseTodos excludes task items inside exclude regions", () => {
386+
// Arrange
387+
const content = `- [ ] Included
388+
%% exclude: start %%
389+
- [ ] Excluded
390+
- [.] Also excluded
391+
%% exclude: end %%
392+
- [ ] Also included`;
393+
394+
const mockFileService = new MockFileService([]);
395+
396+
// Act
397+
const todoService = new TodoService(mockFileService, MOCK_SETTINGS);
398+
const actual = todoService.parseTodos(content);
399+
400+
// Assert
401+
expect(actual).toEqual([
402+
{
403+
task: TaskType.NotStarted,
404+
text: "Included",
405+
indentation: "",
406+
lineno: 0,
407+
} as Todo,
408+
{
409+
task: TaskType.NotStarted,
410+
text: "Also included",
411+
indentation: "",
412+
lineno: 5,
413+
} as Todo,
414+
]);
415+
});
416+
417+
test("parseTodos handles multiple exclude regions", () => {
418+
// Arrange
419+
const content = `- [ ] First
420+
%% exclude: start %%
421+
- [ ] Excluded 1
422+
%% exclude: end %%
423+
- [ ] Second
424+
%% exclude: start %%
425+
- [ ] Excluded 2
426+
%% exclude: end %%
427+
- [ ] Third`;
428+
429+
const mockFileService = new MockFileService([]);
430+
431+
// Act
432+
const todoService = new TodoService(mockFileService, MOCK_SETTINGS);
433+
const actual = todoService.parseTodos(content);
434+
435+
// Assert
436+
expect(actual.map((t) => t.text)).toEqual(["First", "Second", "Third"]);
437+
});
438+
439+
test("parseTodos excludes to end of file when region is not closed", () => {
440+
// Arrange
441+
const content = `- [ ] Included
442+
%% exclude: start %%
443+
- [ ] Excluded`;
444+
445+
const mockFileService = new MockFileService([]);
446+
447+
// Act
448+
const todoService = new TodoService(mockFileService, MOCK_SETTINGS);
449+
const actual = todoService.parseTodos(content);
450+
451+
// Assert
452+
expect(actual.map((t) => t.text)).toEqual(["Included"]);
453+
});
454+
383455
test("Whole shebang formats TODO.md correctly with task items nested under normal lists", async () => {
384456
// Arrange
385457
const taskFile = createMockFile(

src/todoService.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,23 @@ class TodoService {
114114
*/
115115
parseTodos(contents: string): Todo[] {
116116
const lines = contents.split(/[\r]?[\n]/);
117+
const beginPattern = this.settings.excludeRegionBegin;
118+
const endPattern = this.settings.excludeRegionEnd;
119+
let inExcludedRegion = false;
117120
const matchesAndIndices = lines
118121
.map((line, index) => {
122+
if (beginPattern && line.trim() === beginPattern) {
123+
inExcludedRegion = true;
124+
}
125+
if (inExcludedRegion) {
126+
if (endPattern && line.trim() === endPattern) {
127+
inExcludedRegion = false;
128+
}
129+
return {
130+
match: null as unknown as RegExpMatchArray,
131+
index,
132+
} as IndexMatch;
133+
}
119134
const match = line.match(TODO_PATTERN);
120135
return { match, index } as IndexMatch;
121136
})

0 commit comments

Comments
 (0)