From e53cdd2415787b58ec6b614133b16f87d3c9e3ed Mon Sep 17 00:00:00 2001 From: Martin Schenck Date: Sun, 18 Apr 2021 14:18:37 +0200 Subject: [PATCH] Improved performance by switching to cachedRead Using cached read should improve performance when reading files to check for tasks. I also cleaned up some code which now uses proper type checking based on the types provided by the obsidian API. Relates to #15. --- src/Obsidian.ts | 31 +++++++++++++++++++++++++------ src/Tasks/Cache.ts | 26 ++++++-------------------- src/Tasks/Commands.ts | 10 ++++++++++ src/Tasks/File.ts | 14 +++++++++----- 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/Obsidian.ts b/src/Obsidian.ts index 99bc088374..3dc34bd800 100644 --- a/src/Obsidian.ts +++ b/src/Obsidian.ts @@ -1,5 +1,6 @@ import { App, + Editor, EventRef, MarkdownPostProcessor, MarkdownPostProcessorContext, @@ -8,6 +9,7 @@ import { TAbstractFile, TFile, Vault, + View, Workspace, } from 'obsidian'; @@ -54,18 +56,30 @@ export class Obsidian { return activeLeaf.view; } - public get editor(): CodeMirror.Editor { - return (this.workspace.activeLeaf.view as any).sourceMode - .cmEditor as CodeMirror.Editor; + public get editor(): Editor | undefined { + const view: View = this.workspace.activeLeaf.view; + if (view instanceof MarkdownView) { + return view.editor; + } else { + return undefined; + } } public getMarkdownFilePaths(): string[] { return this.vault.getMarkdownFiles().map((file) => file.path); } - public async readLines({ path }: { path: string }): Promise { + public async readLines({ + path, + }: { + path: string; + }): Promise { const file = this.vault.getAbstractFileByPath(path); - const fileContent = await this.vault.read(file as TFile); + if (!(file instanceof TFile)) { + return undefined; + } + + const fileContent = await this.vault.cachedRead(file); const fileLines = fileContent.split('\n'); return fileLines; @@ -79,8 +93,13 @@ export class Obsidian { lines: string[]; }): Promise { const file = this.vault.getAbstractFileByPath(path); + if (!(file instanceof TFile)) { + console.error('Tasks: trying to write non-file:', path); + return Promise.resolve(); + } + const fileContent = lines.join('\n'); - return this.vault.modify(file as TFile, fileContent); + return this.vault.modify(file, fileContent); } public addCommand(command: { diff --git a/src/Tasks/Cache.ts b/src/Tasks/Cache.ts index a047fc2908..e03a5202a9 100644 --- a/src/Tasks/Cache.ts +++ b/src/Tasks/Cache.ts @@ -5,7 +5,6 @@ import { REGEX_TASK, Task } from './Task'; export class Cache { private readonly obsidian: Obsidian; private readonly mutex: Mutex; - private readonly markdownRegexp = /.*\.md$/; private readonly subscribedHandlers: { [number: number]: () => void }; private registeredMaxId: number = 0; @@ -49,20 +48,12 @@ export class Cache { private subscribeToFileEvents(): void { this.obsidian.subscribeToCreation(async (path: string) => { - if (!this.markdownRegexp.test(path)) { - return; - } - await this.mutex.runExclusive(async () => { await this.updateFiles({ paths: [path] }); }); }); this.obsidian.subscribeToModification(async (path: string) => { - if (!this.markdownRegexp.test(path)) { - return; - } - await this.mutex.runExclusive(async () => { this.tasks = this.tasks.filter( (task: Task) => task.path !== path, @@ -72,10 +63,6 @@ export class Cache { }); this.obsidian.subscribeToDeletion(async (path: string) => { - if (!this.markdownRegexp.test(path)) { - return; - } - await this.mutex.runExclusive(async () => { this.tasks = this.tasks.filter( (task: Task) => task.path !== path, @@ -86,13 +73,6 @@ export class Cache { this.obsidian.subscribeToRenaming( async (oldPath: string, newPath: string) => { - if ( - !this.markdownRegexp.test(oldPath) || - !this.markdownRegexp.test(newPath) - ) { - return; - } - await this.mutex.runExclusive(async () => { this.tasks = this.tasks.map( (task: Task): Task => { @@ -124,6 +104,12 @@ export class Cache { for (const path of paths) { let precedingHeader: string | undefined = undefined; const lines = await this.obsidian.readLines({ path }); + // If there are no lines, we don't need to parse. + // Could be a directory, for example. + if (lines === undefined) { + continue; + } + let pageIndex = 0; for ( let lineNumber = 0; diff --git a/src/Tasks/Commands.ts b/src/Tasks/Commands.ts index c56b2327ca..160b6e7ed6 100644 --- a/src/Tasks/Commands.ts +++ b/src/Tasks/Commands.ts @@ -21,6 +21,11 @@ export class Commands { } const editor = this.obsidian.editor; + if (editor === undefined) { + // If we are not in an editor, the command shouldn't be shown. + return false; + } + const currentLine = editor.getLine(editor.getCursor().line); const isTasksLine = REGEX_TASK.test(currentLine); @@ -33,7 +38,12 @@ export class Commands { if (path === undefined) { return; } + // We are certain we are in the editor on a tasks line due to the check above. const editor = obsidian.editor; + if (editor === undefined) { + return; + } + const cursorPosition = editor.getCursor(); const lineNumber = cursorPosition.line; this.file.toggleDone({ path, lineNumber }); diff --git a/src/Tasks/File.ts b/src/Tasks/File.ts index 00a72bf058..92da487c6b 100644 --- a/src/Tasks/File.ts +++ b/src/Tasks/File.ts @@ -14,16 +14,20 @@ export class File { }: { path: string; lineNumber: number; - }) { - const fileLines = await this.obsidian.readLines({ path }); - const line = fileLines[lineNumber]; - fileLines[lineNumber] = this.toggleLine({ + }): Promise { + const lines = await this.obsidian.readLines({ path }); + if (lines === undefined) { + return; + } + + const line = lines[lineNumber]; + lines[lineNumber] = this.toggleLine({ line, path, lineNumber, }); - await this.obsidian.writeLines({ path, lines: fileLines }); + await this.obsidian.writeLines({ path, lines }); } private toggleLine({