diff --git a/src/Query/Query.ts b/src/Query/Query.ts index c9bca724b8..bb2430e8dc 100644 --- a/src/Query/Query.ts +++ b/src/Query/Query.ts @@ -1,4 +1,5 @@ -import { LayoutOptions } from '../TaskLayout'; +import type { ExtendParserHook } from 'QueryRenderer'; +import { LayoutOptions } from '../LayoutOptions'; import type { Task } from '../Task'; import type { IQuery } from '../IQuery'; import { getSettings } from '../Config/Settings'; @@ -50,7 +51,7 @@ export class Query implements IQuery { private readonly commentRegexp = /^#.*/; - constructor({ source }: { source: string }) { + constructor({ source, extensions = [] }: { source: string; extensions?: ExtendParserHook[] }) { this.source = source; source .split('\n') @@ -81,6 +82,8 @@ export class Query implements IQuery { break; case this.parseFilter(line): break; + case this.parseExtensions(extensions, line): + break; default: this._error = `do not understand query: ${line}`; } @@ -235,4 +238,16 @@ export class Query implements IQuery { this._error = 'do not understand query grouping'; } } + + private parseExtensions(extensions: ExtendParserHook[], line: string): boolean { + let found = false; + // Let plugins that implement the "extendParse" hook parse the given + // line + extensions.forEach((extendParser: ExtendParserHook) => { + if (extendParser(line, this.layoutOptions)) { + found = true; + } + }); + return found; + } } diff --git a/src/QueryRenderer.ts b/src/QueryRenderer.ts index 4002c2dbcc..d70d18f9a6 100644 --- a/src/QueryRenderer.ts +++ b/src/QueryRenderer.ts @@ -7,6 +7,7 @@ import { replaceTaskWithTasks } from './File'; import { Query } from './Query/Query'; import type { GroupHeading } from './Query/GroupHeading'; import { TaskModal } from './TaskModal'; +import { Task as TaskModel } from './Task'; import type { TasksEvents } from './TasksEvents'; import type { Task } from './Task'; import { DateFallback } from './DateFallback'; @@ -37,6 +38,9 @@ export class QueryRenderer { } } +export type ExtendTaskHook = (listItem: HTMLLIElement, task: Task, api: object) => void; +export type ExtendParserHook = (line: string, layoutOptions: object) => boolean; + class QueryRenderChild extends MarkdownRenderChild { private readonly app: App; private readonly events: TasksEvents; @@ -48,6 +52,9 @@ class QueryRenderChild extends MarkdownRenderChild { private renderEventRef: EventRef | undefined; private queryReloadTimeout: NodeJS.Timeout | undefined; + private extendTask: ExtendTaskHook[]; + private extendParser: ExtendParserHook[]; + constructor({ app, events, @@ -67,18 +74,34 @@ class QueryRenderChild extends MarkdownRenderChild { this.events = events; this.source = source; this.filePath = filePath; + this.extendTask = []; + this.extendParser = []; + + // Cache which plugins implement our extension hooks + // @ts-ignore + Object.keys(this.app.plugins.plugins).forEach((name) => { + // @ts-ignore + const plugin = this.app.plugins.plugins[name]; + if (plugin.extendTask) { + this.extendTask.push(plugin.extendTask); + } + + if (plugin.extendParser) { + this.extendParser.push(plugin.extendParser); + } + }); // The engine is chosen on the basis of the code block language. Currently // there is only the main engine for the plugin, this allows others to be // added later. switch (this.containerEl.className) { case 'block-language-tasks': - this.query = new Query({ source }); + this.query = new Query({ source, extensions: this.extendParser }); this.queryType = 'tasks'; break; default: - this.query = new Query({ source }); + this.query = new Query({ source, extensions: this.extendParser }); this.queryType = 'tasks'; break; } @@ -119,7 +142,7 @@ class QueryRenderChild extends MarkdownRenderChild { const millisecondsToMidnight = midnight.getTime() - now.getTime(); this.queryReloadTimeout = setTimeout(() => { - this.query = new Query({ source: this.source }); + this.query = new Query({ source: this.source, extensions: this.extendParser }); // Process the current cache state: this.events.triggerRequestCacheUpdate(this.render.bind(this)); this.reloadQueryAtMidnight(); @@ -215,6 +238,16 @@ class QueryRenderChild extends MarkdownRenderChild { this.addEditButton(listItem, task); } + // Let plugins that implement the "extendTask" hook extend the + // current task + this.extendTask.forEach((extendTask: ExtendTaskHook) => + extendTask(listItem, task, { + layoutOptions: this.query.layoutOptions, + Task: TaskModel, + replaceTaskWithTasks, + }), + ); + taskList.appendChild(listItem); }