From 0989618723b5703cd488eed45a87e0fea533f66d Mon Sep 17 00:00:00 2001 From: alexandrerbb Date: Sat, 27 Apr 2024 14:12:38 +0200 Subject: [PATCH] Major codebase rework --- src/heading.ts | 23 ------------- src/plugin.ts | 86 +++++++++++-------------------------------------- src/task.ts | 31 ++++++++++++++++++ src/taskList.ts | 46 ++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 90 deletions(-) delete mode 100644 src/heading.ts create mode 100644 src/task.ts create mode 100644 src/taskList.ts diff --git a/src/heading.ts b/src/heading.ts deleted file mode 100644 index b6a5b0a..0000000 --- a/src/heading.ts +++ /dev/null @@ -1,23 +0,0 @@ -export class Heading { - title: string; - hLevel: number; - treeLevel: number; - parent: Heading | null; - - constructor( - title: string, - hLevel: number, - treeLevel: number, - parent: Heading | null, - ) { - Object.assign(this, { title, hLevel, treeLevel, parent }); - } - - getParent(hLevel: number): Heading | undefined { - return this.hLevel < hLevel ? this : this.parent?.getParent(hLevel); - } - - toMarkdown(): string { - return `${"\t".repeat(this.treeLevel)}- [ ] ${this.title}`; - } -} diff --git a/src/plugin.ts b/src/plugin.ts index 77094ad..5552d15 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -10,25 +10,17 @@ import { type TFile, type TFolder, } from "obsidian"; -import { Heading } from "src/heading"; +import { TaskList, TaskListInterface } from "src/taskList"; interface OutlineTaskListPluginSettings { maxNoteCreationReties: number; } -type EditorCallbackFunction = (editor: Editor, view: MarkdownView) => T; - -type PluginEditorCallbackFunction = ({ - editor, - file, - taskList, -}: { +interface PluginEditorCallbackParameters { editor: Editor; file: TFile; - taskList: string; -}) => T; - -type Outline = Heading[]; + tasks: TaskListInterface; +} const DEFAULT_SETTINGS: OutlineTaskListPluginSettings = { maxNoteCreationReties: 200, @@ -40,44 +32,6 @@ export default class OutlineTaskListPlugin extends Plugin { */ settings: OutlineTaskListPluginSettings; - /** - * Parse the headings of the specified markdown content. - */ - static parseOutline(makdownContent: string): Outline { - const lines = makdownContent - .split(/\r?\n/) - .filter((line) => line !== null && line.startsWith("#")); - const outline: Outline = []; - - for (const line of lines) { - const hLevel = (line.match(/^#+/) as RegExpMatchArray)[0].length; - const prevHeading = outline.at(-1); - let parent = null; - - if (prevHeading !== undefined) { - const sign = Math.sign(hLevel - prevHeading.hLevel); - parent = - sign === -1 - ? (parent = prevHeading.getParent(hLevel) || null) - : sign === 0 - ? prevHeading.parent - : prevHeading; // sign === 1 - } - const treeLevel = parent ? parent.treeLevel + 1 : 0; - outline.push( - new Heading(line.replace(/^#+\s*/, ""), hLevel, treeLevel, parent), - ); - } - return outline; - } - - /** - * Build the task list from the specified outline. - */ - static buildTasklist(outline: Outline): string { - return outline.map((heading) => heading.toMarkdown()).join("\r\n"); - } - /** * Create an Obsidian note to store the resulting task list. */ @@ -85,7 +39,7 @@ export default class OutlineTaskListPlugin extends Plugin { originalName: string, folder: TFolder | null, makdownContent: string, - ): Promise { + ): Promise { const dirPath = folder === null ? "" : folder.path + "/"; for (let index = 1; index < this.settings.maxNoteCreationReties; index++) { try { @@ -95,45 +49,43 @@ export default class OutlineTaskListPlugin extends Plugin { makdownContent, ); } catch (e) { - // File already exists. - continue; + continue; // File already exists. } } + throw Error("Maximum note creation retries exceeded."); } /** * Custom editor callback. */ - pluginEditorCallback( - callback: PluginEditorCallbackFunction, - ): EditorCallbackFunction { + pluginEditorCallback( + callback: ({ editor, file, tasks }: PluginEditorCallbackParameters) => T, + ): (editor: Editor, view: MarkdownView) => T { return (editor: Editor, view: MarkdownView) => { const file = view.file; - if (file === null) { - throw Error(); + if (!file) { + throw Error("Cannot find the opened note."); } - const outline = OutlineTaskListPlugin.parseOutline(editor.getValue()); - const taskList = OutlineTaskListPlugin.buildTasklist(outline); - return callback({ editor, file, taskList }); + const tasks = TaskList(); + tasks.parseOutline(editor.getValue()); + return callback({ editor, file, tasks }); }; } async onload() { await this.loadSettings(); - // Insert task list in editor. this.addCommand({ id: "outline-task-list-insert", name: "Convert outline to a task list here.", - editorCallback: this.pluginEditorCallback(({ editor, taskList }) => { - editor.replaceRange(taskList, editor.getCursor()); + editorCallback: this.pluginEditorCallback(({ editor, tasks }) => { + editor.replaceRange(tasks.toMarkdown(), editor.getCursor()); }), }); - // Insert task list a a new note. this.addCommand({ id: "outline-task-list-new-note", name: "Convert outline to a task list in a new note.", - editorCallback: this.pluginEditorCallback(async ({ file, taskList }) => { - await this.createNote(file.basename, file.parent, taskList); + editorCallback: this.pluginEditorCallback(async ({ file, tasks }) => { + await this.createNote(file.basename, file.parent, tasks.toMarkdown()); }), }); } diff --git a/src/task.ts b/src/task.ts new file mode 100644 index 0000000..6f424f7 --- /dev/null +++ b/src/task.ts @@ -0,0 +1,31 @@ +export interface TaskInterface { + title: string; + standardLevel: number; + treeLevel: number; + parent?: TaskInterface; + getParent: (targetLevel: number) => TaskInterface | undefined; + toMarkdown: () => string; +} + +export const Task = ( + title: string, + standardLevel: number, + treeLevel: number, + parent: TaskInterface | undefined, +): TaskInterface => { + const getParent = (targetLevel: number) => + standardLevel < targetLevel + ? Task(title, standardLevel, treeLevel, parent) // this + : parent?.getParent(targetLevel); + + const toMarkdown = () => `${"\t".repeat(treeLevel)}- [ ] ${title}`; + + return { + title, + standardLevel, + treeLevel, + parent, + getParent, + toMarkdown, + }; +}; diff --git a/src/taskList.ts b/src/taskList.ts new file mode 100644 index 0000000..ea6ccfd --- /dev/null +++ b/src/taskList.ts @@ -0,0 +1,46 @@ +import { Task, TaskInterface } from "src/task"; + +export interface TaskListInterface { + tasks: TaskInterface[]; + parseOutline: (makdownContent: string) => void; + toMarkdown: () => string; +} + +export const TaskList = (): TaskListInterface => { + const tasks: TaskInterface[] = []; + + const getParentTask = (taskLevel: number): TaskInterface | undefined => { + const prevTask = tasks.at(-1); + if (prevTask === undefined) { + return undefined; + } + const sign = Math.sign(taskLevel - prevTask.standardLevel) as -1 | 0 | 1; + switch (sign) { + case -1: + return prevTask.getParent(taskLevel); + case 0: + return prevTask.parent; + case 1: + return prevTask; + } + }; + + const parseOutline = function (makdownContent: string): void { + const lines = makdownContent + .split(/\r?\n/) + .filter((line) => line && line.startsWith("#")); + + for (const line of lines) { + const taskLevel = (line.match(/^#+/) as RegExpMatchArray)[0].length; + const parent = getParentTask(taskLevel); + const treeLevel = parent ? parent.treeLevel + 1 : 0; + tasks.push( + Task(line.replace(/^#+\s*/, ""), taskLevel, treeLevel, parent), + ); + } + }; + + const toMarkdown = () => tasks.map((task) => task.toMarkdown()).join("\r\n"); + + return { tasks, parseOutline, toMarkdown }; +};