Major codebase rework
This commit is contained in:
parent
4a9457efd7
commit
0989618723
|
@ -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}`;
|
||||
}
|
||||
}
|
|
@ -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<T> = (editor: Editor, view: MarkdownView) => T;
|
||||
|
||||
type PluginEditorCallbackFunction<T> = ({
|
||||
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<TFile | undefined> {
|
||||
): Promise<TFile> {
|
||||
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<T>(
|
||||
callback: PluginEditorCallbackFunction<T>,
|
||||
): EditorCallbackFunction<T | undefined> {
|
||||
pluginEditorCallback<T = unknown>(
|
||||
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());
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
|
@ -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 };
|
||||
};
|
Loading…
Reference in New Issue