Skip to content
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
28 changes: 14 additions & 14 deletions src/engine/CaptureChoiceEngine.notice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,20 @@ vi.mock("../formatters/captureChoiceFormatter", () => {
};
});

vi.mock("../utilityObsidian", () => ({
appendToCurrentLine: vi.fn(),
getMarkdownFilesInFolder: vi.fn(async () => []),
getMarkdownFilesWithTag: vi.fn(async () => []),
insertFileLinkToActiveView: vi.fn(),
insertOnNewLineAbove: vi.fn(),
insertOnNewLineBelow: vi.fn(),
isFolder: vi.fn(() => false),
openExistingFileTab: vi.fn(() => false),
openFile: vi.fn(),
overwriteTemplaterOnce: vi.fn(),
templaterParseTemplate: vi.fn(async (_app, content) => content),
getTemplater: vi.fn(() => ({})),
}));
vi.mock("../utilityObsidian", () => ({
appendToCurrentLine: vi.fn(),
getMarkdownFilesInFolder: vi.fn(async () => []),
getMarkdownFilesWithTag: vi.fn(async () => []),
insertFileLinkToActiveView: vi.fn(),
insertOnNewLineAbove: vi.fn(),
insertOnNewLineBelow: vi.fn(),
isFolder: vi.fn(() => false),
openExistingFileTab: vi.fn(() => null),
openFile: vi.fn(),
overwriteTemplaterOnce: vi.fn(),
templaterParseTemplate: vi.fn(async (_app, content) => content),
getTemplater: vi.fn(() => ({})),
}));

vi.mock("three-way-merge", () => ({
default: vi.fn(() => ({})),
Expand Down
19 changes: 11 additions & 8 deletions src/engine/CaptureChoiceEngine.template-property-types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,17 @@ vi.mock("../utilityObsidian", () => ({
getMarkdownFilesInFolder: vi.fn().mockResolvedValue([]),
getMarkdownFilesWithTag: vi.fn().mockResolvedValue([]),
insertFileLinkToActiveView: vi.fn(),
insertOnNewLineAbove: vi.fn(),
insertOnNewLineBelow: vi.fn(),
isFolder: vi.fn().mockReturnValue(false),
openExistingFileTab: vi.fn().mockReturnValue(false),
openFile: vi.fn(),
overwriteTemplaterOnce: vi.fn().mockResolvedValue(undefined),
templaterParseTemplate: vi.fn(async (_app, content) => content),
getTemplater: vi.fn(() => ({})),
insertOnNewLineAbove: vi.fn(),
insertOnNewLineBelow: vi.fn(),
isTemplaterTriggerOnCreateEnabled: vi.fn().mockReturnValue(false),
jumpToNextTemplaterCursorIfPossible: vi.fn().mockResolvedValue(undefined),
isFolder: vi.fn().mockReturnValue(false),
openExistingFileTab: vi.fn().mockReturnValue(null),
openFile: vi.fn(),
overwriteTemplaterOnce: vi.fn().mockResolvedValue(undefined),
templaterParseTemplate: vi.fn(async (_app, content) => content),
waitForFileToStopChanging: vi.fn().mockResolvedValue(undefined),
getTemplater: vi.fn(() => ({})),
}));

describe("CaptureChoiceEngine template property types", () => {
Expand Down
19 changes: 16 additions & 3 deletions src/engine/CaptureChoiceEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ import {
insertFileLinkToActiveView,
insertOnNewLineAbove,
insertOnNewLineBelow,
isTemplaterTriggerOnCreateEnabled,
jumpToNextTemplaterCursorIfPossible,
isFolder,
openExistingFileTab,
openFile,
overwriteTemplaterOnce,
templaterParseTemplate,
waitForTemplaterTriggerOnCreateToComplete,
} from "../utilityObsidian";
import { isCancellationError, reportError } from "../utils/errorUtils";
import { QuickAddChoiceEngine } from "./QuickAddChoiceEngine";
Expand Down Expand Up @@ -157,7 +160,9 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
}
} else {
await this.app.vault.modify(file, newFileContent);
await overwriteTemplaterOnce(this.app, file);
if (this.choice.templater?.afterCapture === "wholeFile") {
await overwriteTemplaterOnce(this.app, file);
}
await this.applyCapturePropertyVars(file);
}

Expand All @@ -174,11 +179,14 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
}

if (this.choice.openFile && file) {
const openExistingTab = openExistingFileTab(this.app, file);
const focus = this.choice.fileOpening.focus ?? true;
const openExistingTab = openExistingFileTab(this.app, file, focus);

if (!openExistingTab) {
await openFile(this.app, file, this.choice.fileOpening);
}

await jumpToNextTemplaterCursorIfPossible(this.app, file);
}
} catch (err) {
if (
Expand Down Expand Up @@ -440,7 +448,10 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
}

// Create the new file with the (optional) template content
const file: TFile = await this.createFileWithInput(filePath, fileContent);
const file: TFile = await this.createFileWithInput(filePath, fileContent, {
suppressTemplaterOnCreate:
this.choice.createFileIfItDoesntExist.createWithTemplate,
});

// Post-process front matter for template property types if we used a template
if (this.choice.createFileIfItDoesntExist.createWithTemplate &&
Expand All @@ -455,6 +466,8 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
fileContent
) {
await overwriteTemplaterOnce(this.app, file);
} else if (isTemplaterTriggerOnCreateEnabled(this.app)) {
await waitForTemplaterTriggerOnCreateToComplete(this.app, file);
}

// Read the file fresh from disk to avoid any potential cached content
Expand Down
17 changes: 13 additions & 4 deletions src/engine/QuickAddEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { App } from "obsidian";
import { TFile, TFolder } from "obsidian";
import { MARKDOWN_FILE_EXTENSION_REGEX } from "../constants";
import { log } from "../logger/logManager";
import { withTemplaterFileCreationSuppressed } from "../utilityObsidian";
import { coerceYamlValue } from "../utils/yamlValues";
import { TemplatePropertyCollector } from "../utils/TemplatePropertyCollector";

Expand Down Expand Up @@ -204,7 +205,8 @@ export abstract class QuickAddEngine {

protected async createFileWithInput(
filePath: string,
fileContent: string
fileContent: string,
opts: { suppressTemplaterOnCreate?: boolean } = {},
): Promise<TFile> {
const dirMatch = filePath.match(/(.*)[/\\]/);
let dirName = "";
Expand All @@ -215,10 +217,17 @@ export abstract class QuickAddEngine {
if (!dir || !(dir instanceof TFolder)) {
await this.createFolder(dirName);

}
}

return await this.app.vault.create(filePath, fileContent);
}
const createFile = () => this.app.vault.create(filePath, fileContent);
const shouldSuppress =
opts.suppressTemplaterOnCreate &&
filePath.toLowerCase().endsWith(".md");

return shouldSuppress
? await withTemplaterFileCreationSuppressed(this.app, filePath, createFile)
: await createFile();
}

/**
* Determines if a file's front matter should be post-processed for template property types.
Expand Down
16 changes: 8 additions & 8 deletions src/engine/TemplateChoiceEngine.notice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ vi.mock("../formatters/completeFormatter", () => {
};
});

vi.mock("../utilityObsidian", () => ({
getTemplater: vi.fn(() => ({})),
overwriteTemplaterOnce: vi.fn(),
getAllFolderPathsInVault: vi.fn(async () => []),
insertFileLinkToActiveView: vi.fn(),
openExistingFileTab: vi.fn(() => false),
openFile: vi.fn(),
}));
vi.mock("../utilityObsidian", () => ({
getTemplater: vi.fn(() => ({})),
overwriteTemplaterOnce: vi.fn(),
getAllFolderPathsInVault: vi.fn(async () => []),
insertFileLinkToActiveView: vi.fn(),
openExistingFileTab: vi.fn(() => null),
openFile: vi.fn(),
}));

vi.mock("../gui/GenericSuggester/genericSuggester", () => ({
default: {
Expand Down
10 changes: 9 additions & 1 deletion src/engine/TemplateChoiceEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { normalizeAppendLinkOptions } from "../types/linkPlacement";
import {
getAllFolderPathsInVault,
insertFileLinkToActiveView,
jumpToNextTemplaterCursorIfPossible,
openExistingFileTab,
openFile,
} from "../utilityObsidian";
Expand Down Expand Up @@ -174,11 +175,18 @@ export class TemplateChoiceEngine extends TemplateEngine {
}

if ((this.choice.openFile || shouldAutoOpen) && createdFile) {
const openExistingTab = openExistingFileTab(this.app, createdFile);
const focus = this.choice.fileOpening.focus ?? true;
const openExistingTab = openExistingFileTab(
this.app,
createdFile,
focus,
);

if (!openExistingTab) {
await openFile(this.app, createdFile, this.choice.fileOpening);
}

await jumpToNextTemplaterCursorIfPossible(this.app, createdFile);
}
} catch (err) {
if (
Expand Down
19 changes: 14 additions & 5 deletions src/engine/TemplateEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type QuickAdd from "../main";
import {
getTemplater,
overwriteTemplaterOnce,
templaterParseTemplate,
} from "../utilityObsidian";
import GenericSuggester from "../gui/GenericSuggester/genericSuggester";
import { MARKDOWN_FILE_EXTENSION_REGEX, CANVAS_FILE_EXTENSION_REGEX } from "../constants";
Expand Down Expand Up @@ -165,9 +166,13 @@ export abstract class TemplateEngine extends QuickAddEngine {
log.logMessage(`Variables: ${Array.from(templateVars.keys()).join(', ')}`);
}

const suppressTemplaterOnCreate = filePath
.toLowerCase()
.endsWith(".md");
const createdFile: TFile = await this.createFileWithInput(
filePath,
formattedTemplateContent
formattedTemplateContent,
{ suppressTemplaterOnCreate },
);

// Post-process front matter for template property types BEFORE Templater
Expand Down Expand Up @@ -252,18 +257,22 @@ export abstract class TemplateEngine extends QuickAddEngine {
const fileBasename = file.basename;
this.formatter.setTitle(fileBasename);

const formattedTemplateContent: string =
let formattedTemplateContent: string =
await this.formatter.formatFileContent(templateContent);
if (file.extension === "md") {
formattedTemplateContent = await templaterParseTemplate(
this.app,
formattedTemplateContent,
file,
);
}
const fileContent: string = await this.app.vault.cachedRead(file);
const newFileContent: string =
section === "top"
? `${formattedTemplateContent}\n${fileContent}`
: `${fileContent}\n${formattedTemplateContent}`;
await this.app.vault.modify(file, newFileContent);

// Process Templater commands
await overwriteTemplaterOnce(this.app, file);

return file;
} catch (err) {
if (isMacroAbortError(err)) {
Expand Down
18 changes: 18 additions & 0 deletions src/gui/ChoiceBuilder/captureChoiceBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder {

// Behavior
new Setting(this.contentEl).setName("Behavior").setHeading();
this.addTemplaterAfterCaptureSetting();
if (!this.choice.captureToActiveFile) {
this.addOpenFileSetting("Open the captured file.");

Expand All @@ -73,6 +74,23 @@ export class CaptureChoiceBuilder extends ChoiceBuilder {
this.addOnePageOverrideSetting(this.choice);
}

private addTemplaterAfterCaptureSetting() {
new Setting(this.contentEl)
.setName("Run Templater on entire destination file after capture")
.setDesc(
"Advanced / legacy: this executes any `<% %>` anywhere in the destination file (including inside code blocks).",
)
.addToggle((toggle) => {
toggle.setValue(this.choice.templater?.afterCapture === "wholeFile");
toggle.onChange((value) => {
if (!this.choice.templater) {
this.choice.templater = {};
}
this.choice.templater.afterCapture = value ? "wholeFile" : "none";
});
});
}

private addCapturedToSetting() {
new Setting(this.contentEl)
.setName("Capture to")
Expand Down
10 changes: 10 additions & 0 deletions src/types/choices/CaptureChoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export class CaptureChoice extends Choice implements ICaptureChoice {
mode: FileViewMode2;
focus: boolean;
};
templater?: {
afterCapture?: "none" | "wholeFile";
};

constructor(name: string) {
super(name, "Capture");
Expand Down Expand Up @@ -70,6 +73,9 @@ export class CaptureChoice extends Choice implements ICaptureChoice {
mode: "default",
focus: true,
};
this.templater = {
afterCapture: "none",
};
}

public static Load(choice: ICaptureChoice): CaptureChoice {
Expand All @@ -78,6 +84,10 @@ export class CaptureChoice extends Choice implements ICaptureChoice {
if (!loaded.activeFileWritePosition) {
loaded.activeFileWritePosition = "cursor";
}
if (!loaded.templater) loaded.templater = { afterCapture: "none" };
if (loaded.templater.afterCapture !== "wholeFile") {
loaded.templater.afterCapture = "none";
}
return loaded;
}
}
3 changes: 3 additions & 0 deletions src/types/choices/ICaptureChoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,7 @@ export default interface ICaptureChoice extends IChoice {
mode: FileViewMode2;
focus: boolean;
};
templater?: {
afterCapture?: "none" | "wholeFile";
};
}
58 changes: 58 additions & 0 deletions src/utilityObsidian.templater-binding.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { describe, expect, it } from "vitest";
import { App, TFile } from "obsidian";
import { jumpToNextTemplaterCursorIfPossible, templaterParseTemplate } from "./utilityObsidian";

describe("templaterParseTemplate", () => {
it("calls parse_template with the correct `this` context", async () => {
const app = new App();
const file = new TFile();
file.path = "QA.md";
file.extension = "md";

const templater = {
functions_generator: { ok: true },
parse_template: async function (
this: any,
_opts: unknown,
content: string,
): Promise<string> {
expect(this?.functions_generator?.ok).toBe(true);
return `rendered:${content}`;
},
};

(app as any).plugins.plugins["templater-obsidian"] = { templater };

const result = await templaterParseTemplate(app as any, "hello", file as any);
expect(result).toBe("rendered:hello");
});
});

describe("jumpToNextTemplaterCursorIfPossible", () => {
it("calls jump_to_next_cursor_location with the correct `this` context", async () => {
const app = new App();
const file = new TFile();
file.path = "QA.md";
file.extension = "md";

(app as any).workspace.getActiveFile = () => file;

const editorHandler = {
plugin: { ok: true },
jump_to_next_cursor_location: async function (
this: any,
_targetFile: unknown,
_autoJump: unknown,
): Promise<void> {
expect(this?.plugin?.ok).toBe(true);
},
};

(app as any).plugins.plugins["templater-obsidian"] = {
settings: { auto_jump_to_cursor: true },
editor_handler: editorHandler,
};

await jumpToNextTemplaterCursorIfPossible(app as any, file as any);
});
});
Loading