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
74 changes: 69 additions & 5 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import TodoService, { Todo } from "src/todoService";

export default class ExtendedTaskListsPlugin extends Plugin {
settings!: ExtendedTaskListsSettings;
private isUpdating = false;

async onload() {
await this.loadSettings();
Expand Down Expand Up @@ -117,20 +118,83 @@ export default class ExtendedTaskListsPlugin extends Plugin {
}

updateTodoFile = async () => {
if (this.isUpdating) return;
this.isUpdating = true;
try {
await this.doUpdateTodoFile();
} finally {
this.isUpdating = false;
}
};

private async doUpdateTodoFile() {
const vault = this.app.vault;
const fileService = new VaultFileService(vault);
const service = new TodoService(fileService, this.settings);
const todoFiles = await service.findTodosFiles();
const todos: Todo[] = todoFiles
const sourceFiles = await service.findTodosFiles();
const allTodos: Todo[] = sourceFiles
.map((todoFile) => {
const todos = service.parseTodos(todoFile.contents);
todos.forEach((todo) => (todo.file = todoFile.file));
return todos;
})
.reduce((prev, cur) => prev.concat(cur), []);
const todoFile = await this.getOrCreateTodoFile(vault);
await service.saveTodos(todoFile as IFile, todos);
};

if (!this.settings.enableNestedTodos) {
const todoFile = await this.getOrCreateTodoFile(vault);
await service.saveTodos(todoFile as IFile, allTodos);
return;
}

const allTodoTargets = await service.findAllTodoFiles();
const isRootTodoFile = (f: IFile) =>
f.path === this.settings.todoFilename ||
f.path === "/" + this.settings.todoFilename;
const rootTodoFile = allTodoTargets.find(isRootTodoFile);
const nestedTodoFiles = allTodoTargets.filter(
(f) => !isRootTodoFile(f),
);

const claimedTodoKeys = new Set<string>();

nestedTodoFiles.sort((a, b) => {
const depthA = a.path.split("/").length;
const depthB = b.path.split("/").length;
return depthB - depthA;
});

for (const nestedTodo of nestedTodoFiles) {
let scopedTodos = service.filterTodosByScope(
allTodos,
nestedTodo.path,
);

if (this.settings.excludeNestedFromParent) {
scopedTodos = scopedTodos.filter(
(todo) =>
!claimedTodoKeys.has(
`${todo.file.path}:${todo.lineno}`,
),
);
for (const todo of scopedTodos) {
claimedTodoKeys.add(`${todo.file.path}:${todo.lineno}`);
}
}

await service.saveTodos(nestedTodo, scopedTodos);
}

const rootFile =
rootTodoFile ?? ((await this.getOrCreateTodoFile(vault)) as IFile);
let rootTodos = allTodos;
if (this.settings.excludeNestedFromParent && claimedTodoKeys.size > 0) {
rootTodos = allTodos.filter(
(todo) =>
!claimedTodoKeys.has(`${todo.file.path}:${todo.lineno}`),
);
}
await service.saveTodos(rootFile, rootTodos);
}

onTodoFileUpdated = async (todoFile: TFile): Promise<boolean> => {
const vault = this.app.vault;
Expand Down
32 changes: 32 additions & 0 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ interface ExtendedTaskListsSettings {
todoFilename: string;
useFullFilepath: boolean;
useHierarchy: boolean;
enableNestedTodos: boolean;
excludeNestedFromParent: boolean;
excludeFilePattern: string;
excludeFolderFilename: string;
excludeRegionBegin: string;
Expand All @@ -18,6 +20,8 @@ const DEFAULT_SETTINGS: ExtendedTaskListsSettings = {
todoFilename: "TODO.md",
useFullFilepath: false,
useHierarchy: false,
enableNestedTodos: false,
excludeNestedFromParent: true,
excludeFilePattern: "<!-- exclude TODO -->",
excludeFolderFilename: ".exclude_todos",
excludeRegionBegin: "%% exclude: start %%",
Expand Down Expand Up @@ -85,6 +89,34 @@ class ExtendedTaskListsSettingTab extends PluginSettingTab {
}),
);

new Setting(containerEl)
.setName("Enable nested TODOs")
.setDesc(
"When enabled, any TODO file you create in a subfolder will be populated with task items from that folder and its subfolders.",
)
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.enableNestedTodos)
.onChange(async (value) => {
this.plugin.settings.enableNestedTodos = value;
await this.plugin.saveSettings();
}),
);

new Setting(containerEl)
.setName("Exclude nested from parent")
.setDesc(
"When enabled, task items that appear in a nested TODO file are excluded from ancestor TODO files to avoid duplication.",
)
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.excludeNestedFromParent)
.onChange(async (value) => {
this.plugin.settings.excludeNestedFromParent = value;
await this.plugin.saveSettings();
}),
);

new Setting(containerEl).setName("Excludes").setHeading();

new Setting(containerEl)
Expand Down
Loading