Skip to content

Commit 9c40c47

Browse files
committed
PR feedback for merging frontmatter properties when inserting a template
refs: #1600, #1218, #1387
1 parent eafa4a7 commit 9c40c47

File tree

4 files changed

+75
-88
lines changed

4 files changed

+75
-88
lines changed

src/core/Templater.ts

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
MarkdownPostProcessorContext,
55
MarkdownView,
66
normalizePath,
7+
stringifyYaml,
78
TAbstractFile,
89
TFile,
910
TFolder,
@@ -14,7 +15,8 @@ import {
1415
get_active_file,
1516
get_folder_path_from_file_path,
1617
resolve_tfile,
17-
merge_front_matter,
18+
get_frontmatter_and_content,
19+
merge_objects,
1820
} from "utils/Utils";
1921
import TemplaterPlugin from "main";
2022
import {
@@ -24,7 +26,6 @@ import {
2426
import { errorWrapper, errorWrapperSync, TemplaterError } from "utils/Error";
2527
import { Parser } from "./parser/Parser";
2628
import { log_error } from "utils/Log";
27-
import * as yaml from "js-yaml";
2829

2930
export enum RunMode {
3031
CreateNewFromTemplate,
@@ -257,21 +258,23 @@ export class Templater {
257258
return;
258259
}
259260

260-
const front_matter_info = getFrontMatterInfo(output_content);
261-
const frontmatter = yaml.load(front_matter_info.frontmatter);
262-
await merge_front_matter(
263-
this.plugin.app,
264-
active_editor.file,
265-
frontmatter
266-
);
261+
const { content, frontmatter } =
262+
get_frontmatter_and_content(output_content);
267263

268264
const editor = active_editor.editor;
269265
const doc = editor.getDoc();
270266
const oldSelections = doc.listSelections();
271-
doc.replaceSelection(
272-
output_content.slice(front_matter_info.contentStart)
273-
);
267+
doc.replaceSelection(content);
274268
if (active_view) {
269+
// Merge frontmatter
270+
if (
271+
Object.keys(frontmatter).length > 0 &&
272+
active_view instanceof MarkdownView &&
273+
typeof active_view.metadataEditor?.insertProperties ===
274+
"function"
275+
) {
276+
active_view.metadataEditor.insertProperties(frontmatter);
277+
}
275278
// Wait for view to finish rendering properties widget
276279
await delay(100);
277280
// Save the file to ensure modifications saved to disk by the time `on_all_templates_executed` callback is executed
@@ -316,29 +319,22 @@ export class Templater {
316319
return;
317320
}
318321

319-
let existing_front_matter = null;
320-
await delay(100); // Sometimes the front matter is not yet available if the file was just created
321-
await this.plugin.app.fileManager.processFrontMatter(
322-
file,
323-
(front_matter) => {
324-
existing_front_matter = front_matter;
322+
const {
323+
content: output_content_body,
324+
frontmatter: output_frontmatter,
325+
} = get_frontmatter_and_content(output_content);
326+
327+
await this.plugin.app.vault.process(file, (data) => {
328+
let result = "";
329+
const { content, frontmatter } = get_frontmatter_and_content(data);
330+
merge_objects(frontmatter, output_frontmatter);
331+
if (Object.keys(frontmatter).length > 0) {
332+
result += `---\n${stringifyYaml(frontmatter)}---\n`;
325333
}
326-
);
327-
328-
const front_matter_info = getFrontMatterInfo(output_content);
329-
const frontmatter = yaml.load(front_matter_info.frontmatter);
330-
331-
if (existing_front_matter) {
332-
// Bases can create frontmatter, merge this into the template frontmatter
333-
await merge_front_matter(this.plugin.app, file, frontmatter);
334-
await this.plugin.app.vault.append(
335-
file,
336-
output_content.slice(front_matter_info.contentStart)
337-
);
338-
output_content = await this.plugin.app.vault.read(file);
339-
} else {
340-
await this.plugin.app.vault.modify(file, output_content);
341-
}
334+
result += content + output_content_body;
335+
output_content = result;
336+
return result;
337+
});
342338
// Set cursor to first line of editor (below properties)
343339
// https://github.com/SilentVoid13/Templater/issues/1231
344340
if (

src/core/functions/internal_functions/file/InternalModuleFile.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { InternalModule } from "../InternalModule";
22
import { log_error } from "utils/Log";
3-
import * as yaml from "js-yaml";
43
import {
54
FileSystemAdapter,
65
getAllTags,
7-
getFrontMatterInfo,
86
moment,
97
normalizePath,
108
parseLinktext,
@@ -15,7 +13,6 @@ import {
1513
} from "obsidian";
1614
import { TemplaterError } from "utils/Error";
1715
import { ModuleName } from "editor/TpDocumentation";
18-
import { merge_front_matter } from "utils/Utils";
1916

2017
export const DEPTH_LIMIT = 10;
2118

@@ -213,20 +210,14 @@ export class InternalModuleFile extends InternalModule {
213210
}
214211
}
215212

216-
const active_file = this.plugin.app.workspace.getActiveFile();
217-
218213
try {
219214
const parsed_content =
220215
await this.plugin.templater.parser.parse_commands(
221216
inc_file_content,
222217
this.plugin.templater.current_functions_object
223218
);
224-
const front_matter_info = getFrontMatterInfo(parsed_content);
225-
const frontmatter = yaml.load(front_matter_info.frontmatter);
226-
await merge_front_matter(this.plugin.app, active_file, frontmatter);
227-
228219
this.include_depth -= 1;
229-
return parsed_content.slice(front_matter_info.contentStart);
220+
return parsed_content;
230221
} catch (e) {
231222
this.include_depth -= 1;
232223
throw e;

src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ declare module "obsidian" {
4848
from: number;
4949
to: number;
5050
}
51+
52+
interface MarkdownView {
53+
metadataEditor?: {
54+
insertProperties?: (properties: Record<string, unknown>) => void;
55+
};
56+
}
5157
}
5258

5359
export {};

src/utils/Utils.ts

Lines changed: 38 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import {
22
DocBlock,
33
DocNode,
4-
DocParamBlock,
54
DocParamCollection,
65
DocPlainText,
76
DocSection,
8-
ParserContext,
97
TSDocParser,
108
} from "@microsoft/tsdoc";
119

@@ -14,7 +12,9 @@ import { TJDocFile, TJDocFileArgument } from "./TJDocFile";
1412
import { TemplaterError } from "./Error";
1513
import {
1614
App,
15+
getFrontMatterInfo,
1716
normalizePath,
17+
parseYaml,
1818
TAbstractFile,
1919
TFile,
2020
TFolder,
@@ -241,49 +241,43 @@ export function append_bolded_label_with_value_to_parent(
241241
return para;
242242
}
243243

244-
export async function merge_front_matter(
245-
app: App,
246-
file: TFile | null,
247-
properties: Record<string, unknown>
248-
): Promise<void> {
249-
if (!file || !is_object(properties)) {
250-
return;
251-
}
252-
try {
253-
await app.fileManager.processFrontMatter(file, (frontmatter) => {
254-
for (const prop in properties) {
255-
const currentValue = frontmatter[prop];
256-
const newValue = properties[prop];
257-
258-
if (currentValue === undefined) {
259-
// If the property doesn't exist, add it
260-
frontmatter[prop] = newValue;
261-
} else if (
262-
Array.isArray(currentValue) ||
263-
Array.isArray(newValue)
264-
) {
265-
// If either is an array, merge them
266-
frontmatter[prop] = Array.from(
267-
new Set([
268-
...(currentValue
269-
? Array.isArray(currentValue)
270-
? currentValue
271-
: [currentValue]
272-
: []),
273-
...(newValue
274-
? Array.isArray(newValue)
275-
? newValue
276-
: [newValue]
277-
: []),
278-
])
279-
);
280-
} else if (newValue !== currentValue && newValue != null) {
281-
// If they are different, update the value
282-
frontmatter[prop] = newValue;
244+
/**
245+
* Merges two objects recursively. Target object will be modified.
246+
* @param target The target object to merge into.
247+
* @param source The source object to merge from.
248+
*/
249+
export function merge_objects(
250+
target: Record<string, unknown>,
251+
source: Record<string, unknown>
252+
) {
253+
if (Object.keys(source).length === 0) return;
254+
for (const key in source) {
255+
if (source.hasOwnProperty(key)) {
256+
if (target.hasOwnProperty(key)) {
257+
const targetValue = target[key];
258+
const sourceValue = source[key];
259+
if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
260+
target[key] = targetValue.concat(sourceValue).unique();
261+
} else if (is_object(targetValue) && is_object(sourceValue)) {
262+
merge_objects(targetValue, sourceValue);
263+
} else {
264+
target[key] = sourceValue;
283265
}
266+
} else {
267+
target[key] = source[key];
284268
}
285-
});
286-
} catch (error) {
287-
console.error("Error in processing frontmatter: ", error);
269+
}
270+
}
271+
}
272+
273+
export function get_frontmatter_and_content(content: string) {
274+
let frontmatter: Record<string, unknown> = {};
275+
const front_matter_info = getFrontMatterInfo(content);
276+
if (front_matter_info.frontmatter) {
277+
frontmatter = parseYaml(front_matter_info.frontmatter);
288278
}
279+
return {
280+
frontmatter,
281+
content: content.slice(front_matter_info.contentStart),
282+
};
289283
}

0 commit comments

Comments
 (0)