Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hooks module #1241

Merged
merged 3 commits into from
Nov 13, 2023
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
19 changes: 19 additions & 0 deletions docs/documentation.toml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,25 @@ description = "Retrieves the file's title."
name = "frontmatter"
description = "This modules exposes all the frontmatter variables of a file as variables."



[tp.hooks]
name = "hooks"
description = "This module exposes hooks that allow you to execute code when a Templater event occurs."

[tp.hooks.functions.on_all_templates_executed]
name = "on_all_templates_executed"
description = """Hooks into when all actively running templates have finished executing. Most of the time this will be a single template, unless you are using `tp.file.include` or `tp.file.create_new`.

Multiple invokations of this method will have their callback functions run in parallel."""
definition = "tp.hooks.on_all_templates_executed(callback_function: () => any)"

[[tp.hooks.functions.on_all_templates_executed.args]]
name = "callback_function"
description = "Callback function that will be executed when all actively running templates have finished executing."



[tp.obsidian]
name = "obsidian"
description = "This module exposes all the functions and classes from the obsidian API."
Expand Down
1 change: 1 addition & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- [tp.date](./internal-functions/internal-modules/date-module.md)
- [tp.file](./internal-functions/internal-modules/file-module.md)
- [tp.frontmatter](./internal-functions/internal-modules/frontmatter-module.md)
- [tp.hooks](./internal-functions/internal-modules/hooks-module.md)
- [tp.obsidian](./internal-functions/internal-modules/obsidian-module.md)
- [tp.system](./internal-functions/internal-modules/system-module.md)
- [tp.web](./internal-functions/internal-modules/web-module.md)
Expand Down
52 changes: 52 additions & 0 deletions docs/src/internal-functions/internal-modules/hooks-module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Hooks Module

{{ tp.hooks.description }}

<!-- toc -->

## Documentation

Function documentation is using a specific syntax. More information [here](../../syntax.md#function-documentation-syntax)


{%- for key, fn in tp.hooks.functions %}
### `{{ fn.definition }}`

{{ fn.description }}

{% if fn.args %}
##### Arguments

{% for arg in fn.args %}
- `{{ arg.name }}`: {{ arg.description }}
{% endfor %}
{% endif %}

{% if fn.example %}
##### Example

```
{{ fn.example }}
```
{% endif %}
{%- endfor %}

## Examples

```javascript
// Update frontmatter after template finishes executing
<%*
tp.hooks.on_all_templates_executed(async () => {
const file = tp.file.find_tfile(tp.file.path(true));
await app.fileManager.processFrontMatter(file, (frontmatter) => {
frontmatter["key"] = "value";
});
});
%>
// Run a command from another plugin that modifies the current file, after Templater has updated the file
<%*
tp.hooks.on_all_templates_executed(() => {
app.commands.executeCommandById("obsidian-linter:lint-file");
});
-%>
```
1 change: 1 addition & 0 deletions docs/src/internal-functions/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The different internal variables and functions offered by [Templater](https://gi
- [Date module](./internal-modules/date-module.md): `tp.date`
- [File module](./internal-modules/file-module.md): `tp.file`
- [Frontmatter module](./internal-modules/frontmatter-module.md): `tp.frontmatter`
- [Hooks module](./internal-modules/hooks-module.md): `tp.hooks`
- [Obsidian module](./internal-modules/obsidian-module.md): `tp.obsidian`
- [System module](./internal-modules/system-module.md): `tp.system`
- [Web module](./internal-modules/web-module.md): `tp.web`
Expand Down
45 changes: 41 additions & 4 deletions src/core/Templater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
MarkdownPostProcessorContext,
MarkdownView,
normalizePath,
requireApiVersion,
TAbstractFile,
TFile,
TFolder,
Expand Down Expand Up @@ -42,13 +41,15 @@ export class Templater {
public parser: Parser;
public functions_generator: FunctionsGenerator;
public current_functions_object: Record<string, unknown>;
private templater_task_counter: number;

constructor(private plugin: TemplaterPlugin) {
this.functions_generator = new FunctionsGenerator(this.plugin);
this.parser = new Parser();
}

async setup(): Promise<void> {
this.templater_task_counter = 0;
await this.parser.init();
await this.functions_generator.init();
this.plugin.registerMarkdownPostProcessor((el, ctx) =>
Expand Down Expand Up @@ -94,12 +95,25 @@ export class Templater {
return content;
}

private start_templater_task() {
this.templater_task_counter++;
}

private async end_templater_task() {
this.templater_task_counter--;
if (this.templater_task_counter === 0) {
app.workspace.trigger("templater:all-templates-executed");
await this.functions_generator.teardown();
}
}

async create_new_note_from_template(
template: TFile | string,
folder?: TFolder,
filename?: string,
open_new_note = true
): Promise<TFile | undefined> {
this.start_templater_task();
// TODO: Maybe there is an obsidian API function for that
if (!folder) {
const new_file_location = app.vault.getConfig("newFileLocation");
Expand All @@ -123,11 +137,20 @@ export class Templater {
}

// TODO: Change that, not stable atm
const created_note = await app.fileManager.createNewMarkdownFile(
folder,
filename ?? "Untitled"
const created_note = await errorWrapper(
async () =>
app.fileManager.createNewMarkdownFile(
folder,
filename ?? "Untitled"
),
"Couldn't create markdown file."
);

if (created_note == null) {
await this.end_templater_task();
return;
}

let running_config: RunningConfig;
let output_content: string;
if (template instanceof TFile) {
Expand All @@ -154,6 +177,7 @@ export class Templater {

if (output_content == null) {
await app.vault.delete(created_note);
await this.end_templater_task();
return;
}

Expand Down Expand Up @@ -184,16 +208,19 @@ export class Templater {
});
}

await this.end_templater_task();
return created_note;
}

async append_template_to_active_file(template_file: TFile): Promise<void> {
this.start_templater_task();
const active_view = app.workspace.getActiveViewOfType(MarkdownView);
const active_editor = app.workspace.activeEditor;
if (!active_editor || !active_editor.file || !active_editor.editor) {
log_error(
new TemplaterError("No active editor, can't append templates.")
);
await this.end_templater_task();
return;
}
const running_config = this.create_running_config(
Expand All @@ -207,13 +234,15 @@ export class Templater {
);
// errorWrapper failed
if (output_content == null) {
await this.end_templater_task();
return;
}

const editor = active_editor.editor;
const doc = editor.getDoc();
const oldSelections = doc.listSelections();
doc.replaceSelection(output_content);
await app.vault.modify(active_editor.file, editor.getValue());

app.workspace.trigger("templater:template-appended", {
view: active_view,
Expand All @@ -227,12 +256,14 @@ export class Templater {
active_editor.file,
true
);
await this.end_templater_task();
}

async write_template_to_file(
template_file: TFile,
file: TFile
): Promise<void> {
this.start_templater_task();
const active_editor = app.workspace.activeEditor;
const running_config = this.create_running_config(
template_file,
Expand Down Expand Up @@ -260,6 +291,7 @@ export class Templater {
file,
true
);
await this.end_templater_task();
}

overwrite_active_file_commands(): void {
Expand All @@ -279,6 +311,7 @@ export class Templater {
file: TFile,
active_file = false
): Promise<void> {
this.start_templater_task();
const running_config = this.create_running_config(
file,
file,
Expand All @@ -290,6 +323,7 @@ export class Templater {
);
// errorWrapper failed
if (output_content == null) {
await this.end_templater_task();
return;
}
await app.vault.modify(file, output_content);
Expand All @@ -301,6 +335,7 @@ export class Templater {
file,
true
);
await this.end_templater_task();
}

async process_dynamic_templates(
Expand Down Expand Up @@ -451,6 +486,7 @@ export class Templater {
if (!file) {
continue;
}
this.start_templater_task();
const running_config = this.create_running_config(
file,
file,
Expand All @@ -460,6 +496,7 @@ export class Templater {
async () => this.read_and_parse_template(running_config),
`Startup Template parsing error, aborting.`
);
await this.end_templater_task();
}
}
}
4 changes: 4 additions & 0 deletions src/core/functions/FunctionsGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export class FunctionsGenerator implements IGenerateObject {
await this.internal_functions.init();
}

async teardown(): Promise<void> {
await this.internal_functions.teardown();
}

additional_functions(): Record<string, unknown> {
return {
obsidian: obsidian_module,
Expand Down
8 changes: 8 additions & 0 deletions src/core/functions/internal_functions/InternalFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { InternalModule } from "./InternalModule";
import { InternalModuleDate } from "./date/InternalModuleDate";
import { InternalModuleFile } from "./file/InternalModuleFile";
import { InternalModuleWeb } from "./web/InternalModuleWeb";
import { InternalModuleHooks } from "./hooks/InternalModuleHooks";
import { InternalModuleFrontmatter } from "./frontmatter/InternalModuleFrontmatter";
import { InternalModuleSystem } from "./system/InternalModuleSystem";
import { RunningConfig } from "core/Templater";
Expand All @@ -17,6 +18,7 @@ export class InternalFunctions implements IGenerateObject {
this.modules_array.push(new InternalModuleFile(this.plugin));
this.modules_array.push(new InternalModuleWeb(this.plugin));
this.modules_array.push(new InternalModuleFrontmatter(this.plugin));
this.modules_array.push(new InternalModuleHooks(this.plugin));
this.modules_array.push(new InternalModuleSystem(this.plugin));
this.modules_array.push(new InternalModuleConfig(this.plugin));
}
Expand All @@ -27,6 +29,12 @@ export class InternalFunctions implements IGenerateObject {
}
}

async teardown(): Promise<void> {
for (const mod of this.modules_array) {
await mod.teardown();
}
}

async generate_object(
config: RunningConfig
): Promise<Record<string, unknown>> {
Expand Down
1 change: 1 addition & 0 deletions src/core/functions/internal_functions/InternalModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export abstract class InternalModule implements IGenerateObject {

abstract create_static_templates(): Promise<void>;
abstract create_dynamic_templates(): Promise<void>;
abstract teardown(): Promise<void>;

async init(): Promise<void> {
await this.create_static_templates();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export class InternalModuleConfig extends InternalModule {

async create_dynamic_templates(): Promise<void> {}

async teardown(): Promise<void> {}

async generate_object(
config: RunningConfig
): Promise<Record<string, unknown>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export class InternalModuleDate extends InternalModule {

async create_dynamic_templates(): Promise<void> {}

async teardown(): Promise<void> {}

generate_now(): (
format?: string,
offset?: number | string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export class InternalModuleFile extends InternalModule {
this.dynamic_functions.set("title", this.generate_title());
}

async teardown(): Promise<void> {}

async generate_content(): Promise<string> {
return await app.vault.read(this.config.target_file);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ export class InternalModuleFrontmatter extends InternalModule {
Object.entries(cache?.frontmatter || {})
);
}

async teardown(): Promise<void> {}
}
38 changes: 38 additions & 0 deletions src/core/functions/internal_functions/hooks/InternalModuleHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { EventRef } from "obsidian";
import { ModuleName } from "editor/TpDocumentation";
import { InternalModule } from "../InternalModule";

export class InternalModuleHooks extends InternalModule {
public name: ModuleName = "hooks";
private event_refs: EventRef[] = [];

async create_static_templates(): Promise<void> {
this.static_functions.set(
"on_all_templates_executed",
this.generate_on_all_templates_executed()
);
}

async create_dynamic_templates(): Promise<void> {}

async teardown(): Promise<void> {
this.event_refs.forEach((eventRef) => {
eventRef.e.offref(eventRef);
});
this.event_refs = [];
}

generate_on_all_templates_executed(): (
callback_function: () => unknown
) => void {
return (callback_function) => {
const event_ref = app.workspace.on(
"templater:all-templates-executed",
() => callback_function()
);
if (event_ref) {
this.event_refs.push(event_ref);
}
};
}
}
Loading
Loading