Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const DEFAULT_SETTINGS: ExtendedTaskListsSettings = {
useHierarchy: false,
excludeFilePattern: "<!-- exclude TODO -->",
excludeFolderFilename: ".exclude_todos",
excludeRegionBegin: "%% exclude: start %%",
excludeRegionEnd: "%% exclude: end %%",
excludeRegionBegin: "<!-- exclude: start -->",
excludeRegionEnd: "<!-- exclude: end -->",
includeNotStarted: true,
includeInProgress: true,
includeWontDo: false,
Expand Down Expand Up @@ -88,29 +88,29 @@ class ExtendedTaskListsSettingTab extends PluginSettingTab {
new Setting(containerEl).setName("Excludes").setHeading();

new Setting(containerEl)
.setName("Exclude file pattern")
.setName("Exclude folder filename")
.setDesc(
"A pattern that should be inserted anywhere in a Markdown file to exclude it from the generated TODO file.",
'The filename to add to a folder to exclude all task lists in it from the generated TODO file. You may prefer to change the default value since dot files (files that start with a ".") do not show up within Obsidian.',
)
.addText((text) =>
text
.setValue(this.plugin.settings.excludeFilePattern)
.setValue(this.plugin.settings.excludeFolderFilename)
.onChange(async (value) => {
this.plugin.settings.excludeFilePattern = value;
this.plugin.settings.excludeFolderFilename = value;
await this.plugin.saveSettings();
}),
);

new Setting(containerEl)
.setName("Exclude folder filename")
.setName("Exclude file pattern")
.setDesc(
'The filename to add to a folder to exclude all task lists in it from the generated TODO file. You may prefer to change the default value since dot files (files that start with a ".") do not show up within Obsidian.',
"A pattern that should be inserted anywhere in a Markdown file to exclude it from the generated TODO file.",
)
.addText((text) =>
text
.setValue(this.plugin.settings.excludeFolderFilename)
.setValue(this.plugin.settings.excludeFilePattern)
.onChange(async (value) => {
this.plugin.settings.excludeFolderFilename = value;
this.plugin.settings.excludeFilePattern = value;
await this.plugin.saveSettings();
}),
);
Expand Down
53 changes: 53 additions & 0 deletions src/todoService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,27 @@ class MockFileService implements IFileService {
}

describe("TodoService", () => {
test("findTodosFiles excludes files with alternate comment style", async () => {
// Arrange
const taskFile = createMockFile("test.md", "- [ ] task item");
const fileWithObsidianExclude = createMockFile(
"Exclude.md",
"%% exclude TODO %%",
);

const files = [taskFile, fileWithObsidianExclude];
const mockFileService = new MockFileService(files);

// Act
const todoService = new TodoService(mockFileService, MOCK_SETTINGS);
const actual = await todoService.findTodosFiles();

// Assert
expect(actual).toEqual([
{ file: taskFile, contents: "- [ ] task item" } as TodoFile,
]);
});

test("findTodosFiles excludes files", async () => {
// Arrange
const taskFile = createMockFile("test.md", "- [ ] task item");
Expand Down Expand Up @@ -383,6 +404,38 @@ describe("TodoService", () => {
expect(actual).toEqual(expected);
});

test("parseTodos excludes task items inside HTML-style 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 excludes task items inside exclude regions", () => {
// Arrange
const content = `- [ ] Included
Expand Down
36 changes: 31 additions & 5 deletions src/todoService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,24 @@ interface IndexMatch {
const TODO_PATTERN = /^(?<indentation>\s*)-\s?\[(?<task>.)\]\s+(?<text>.*)$/;
const LINK_PATTERN = /^- \[.*\]\((?<path>.*)\)$/;

function alternateCommentStyle(pattern: string): string | null {
const obsidianMatch = pattern.match(/^%%\s*(.*?)\s*%%$/);
if (obsidianMatch) {
return `<!-- ${obsidianMatch[1]} -->`;
}
const htmlMatch = pattern.match(/^<!--\s*(.*?)\s*-->$/);
if (htmlMatch) {
return `%% ${htmlMatch[1]} %%`;
}
return null;
}

function matchesEitherCommentStyle(line: string, pattern: string): boolean {
if (line === pattern) return true;
const alt = alternateCommentStyle(pattern);
return alt != null && line === alt;
}

class TodoService {
private fileService: IFileService;
private settings: ExtendedTaskListsSettings;
Expand Down Expand Up @@ -72,9 +90,11 @@ class TodoService {
(todoFile) =>
!todoFile.contents
.split(/[\r\n]+/)
.some(
(line) =>
line.trim() === this.settings.excludeFilePattern,
.some((line) =>
matchesEitherCommentStyle(
line.trim(),
this.settings.excludeFilePattern,
),
),
);
return todoFiles;
Expand Down Expand Up @@ -119,11 +139,17 @@ class TodoService {
let inExcludedRegion = false;
const matchesAndIndices = lines
.map((line, index) => {
if (beginPattern && line.trim() === beginPattern) {
if (
beginPattern &&
matchesEitherCommentStyle(line.trim(), beginPattern)
) {
inExcludedRegion = true;
}
if (inExcludedRegion) {
if (endPattern && line.trim() === endPattern) {
if (
endPattern &&
matchesEitherCommentStyle(line.trim(), endPattern)
) {
inExcludedRegion = false;
}
return {
Expand Down