From 7badfda41b95efbb34b8b790d7fa2e5c04da59fa Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Thu, 21 Jul 2022 18:38:53 +0300 Subject: [PATCH] #352 - Custom placeholders now support scripting --- CHANGELOG.md | 1 + src/commands/Article.ts | 6 ++-- src/commands/Template.ts | 2 +- src/helpers/ArticleHelper.ts | 43 +++++++++++++++++++++++++---- src/helpers/ContentType.ts | 10 +++---- src/helpers/CustomScript.ts | 7 ++++- src/listeners/panel/DataListener.ts | 11 ++++++-- src/models/CustomPlaceholder.ts | 6 ++++ src/models/PanelSettings.ts | 2 +- src/models/index.ts | 1 + 10 files changed, 70 insertions(+), 19 deletions(-) create mode 100644 src/models/CustomPlaceholder.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 096f6f20..ac8e1503 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### 🎨 Enhancements +- [#352](https://github.com/estruyf/vscode-front-matter/issues/352): Custom placeholders now support scripting - [#370](https://github.com/estruyf/vscode-front-matter/issues/370): Define the tags and categories as reserved keywords for custom taxonomy - [#372](https://github.com/estruyf/vscode-front-matter/issues/372): Rename Taxonomy tab to Taxonomies - [#374](https://github.com/estruyf/vscode-front-matter/issues/374): Hide the front matter section to use the panel instead diff --git a/src/commands/Article.ts b/src/commands/Article.ts index 00b42508..724a8845 100644 --- a/src/commands/Article.ts +++ b/src/commands/Article.ts @@ -2,7 +2,7 @@ import { DEFAULT_CONTENT_TYPE } from './../constants/ContentType'; import { isValidFile } from './../helpers/isValidFile'; import { SETTING_AUTO_UPDATE_DATE, SETTING_MODIFIED_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TEMPLATES_PREFIX, CONFIG_KEY, SETTING_DATE_FORMAT, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_CONTENT_PLACEHOLDERS, TelemetryEvent } from './../constants'; import * as vscode from 'vscode'; -import { Field, TaxonomyType } from "../models"; +import { CustomPlaceholder, Field, TaxonomyType } from "../models"; import { format } from "date-fns"; import { ArticleHelper, Settings, SlugHelper } from '../helpers'; import { Notifications } from '../helpers/Notifications'; @@ -225,8 +225,8 @@ export class Article { } // Update the fields containing a custom placeholder that depends on slug - const placeholders = Settings.get<{id: string, value: string}[]>(SETTING_CONTENT_PLACEHOLDERS); - const customPlaceholders = placeholders?.filter(p => p.value.includes("{{slug}}")); + const placeholders = Settings.get(SETTING_CONTENT_PLACEHOLDERS); + const customPlaceholders = placeholders?.filter(p => p.value && p.value.includes("{{slug}}")); const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string; for (const customPlaceholder of (customPlaceholders || [])) { const customPlaceholderFields = contentType.fields.filter(f => f.default === `{{${customPlaceholder.id}}}`); diff --git a/src/commands/Template.ts b/src/commands/Template.ts index 8c138964..e7428849 100644 --- a/src/commands/Template.ts +++ b/src/commands/Template.ts @@ -175,7 +175,7 @@ export class Template { } if (frontMatter.data) { - frontMatter.data = ArticleHelper.updatePlaceholders(frontMatter.data, titleValue); + frontMatter.data = await ArticleHelper.updatePlaceholders(frontMatter.data, titleValue, newFilePath); frontMatter = Article.updateDate(frontMatter); diff --git a/src/helpers/ArticleHelper.ts b/src/helpers/ArticleHelper.ts index fc7be101..0100a50f 100644 --- a/src/helpers/ArticleHelper.ts +++ b/src/helpers/ArticleHelper.ts @@ -1,3 +1,4 @@ +import { CustomPlaceholder } from './../models/CustomPlaceholder'; import { Uri, workspace } from 'vscode'; import { MarkdownFoldingProvider } from './../providers/MarkdownFoldingProvider'; import { DEFAULT_CONTENT_TYPE, DEFAULT_CONTENT_TYPE_NAME } from './../constants/ContentType'; @@ -22,6 +23,8 @@ import { fromMarkdown } from 'mdast-util-from-markdown'; import { Link, Parent } from 'mdast-util-from-markdown/lib'; import { Content } from 'mdast'; import { processKnownPlaceholders } from './PlaceholderHelper'; +import { CustomScript } from './CustomScript'; +import { Folders } from '../commands/Folders'; export class ArticleHelper { private static notifiedFiles: string[] = []; @@ -368,7 +371,7 @@ export class ArticleHelper { * @param title * @returns */ - public static updatePlaceholders(data: any, title: string) { + public static async updatePlaceholders(data: any, title: string, filePath: string) { const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string; const fmData = Object.assign({}, data); @@ -384,7 +387,7 @@ export class ArticleHelper { } fmData[fieldName] = processKnownPlaceholders(fmData[fieldName], title, dateFormat); - fmData[fieldName] = this.processCustomPlaceholders(fmData[fieldName], title); + fmData[fieldName] = await this.processCustomPlaceholders(fmData[fieldName], title, filePath); } return fmData; @@ -396,16 +399,44 @@ export class ArticleHelper { * @param title * @returns */ - public static processCustomPlaceholders(value: string, title: string) { + public static async processCustomPlaceholders(value: string, title: string, filePath: string) { if (value && typeof value === "string") { const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string; - const placeholders = Settings.get<{id: string, value: string}[]>(SETTING_CONTENT_PLACEHOLDERS); + const placeholders = Settings.get(SETTING_CONTENT_PLACEHOLDERS); if (placeholders && placeholders.length > 0) { for (const placeholder of placeholders) { if (value.includes(`{{${placeholder.id}}}`)) { + + let placeHolderValue = placeholder.value || ""; + if (placeholder.script) { + const wsFolder = Folders.getWorkspaceFolder(); + const script = { title: placeholder.id, script: placeholder.script, command: placeholder.command }; + let output: string | any = await CustomScript.executeScript(script, wsFolder?.fsPath || "", `'${filePath}' '${title}'`); + + if (output) { + // Check if the output needs to be parsed + if (output.includes("{") && output.includes("}")) { + try { + output = JSON.parse(output); + } catch (e) { + // Do nothing + } + } else { + output = output.split("\n"); + } + + placeHolderValue = output; + } + } + const regex = new RegExp(`{{${placeholder.id}}}`, "g"); - const updatedValue = processKnownPlaceholders(placeholder.value, title, dateFormat); - value = value.replace(regex, updatedValue); + const updatedValue = processKnownPlaceholders(placeHolderValue, title, dateFormat); + + if (value === `{{${placeholder.id}}}`) { + value = updatedValue; + } else { + value = value.replace(regex, updatedValue); + } } } } diff --git a/src/helpers/ContentType.ts b/src/helpers/ContentType.ts index 84a5ed7a..263a4e7a 100644 --- a/src/helpers/ContentType.ts +++ b/src/helpers/ContentType.ts @@ -526,7 +526,7 @@ export class ContentType { } } - let data: any = this.processFields(contentType, titleValue, templateData?.data || {}); + let data: any = await this.processFields(contentType, titleValue, templateData?.data || {}, newFilePath); data = ArticleHelper.updateDates(Object.assign({}, data)); @@ -553,7 +553,7 @@ export class ContentType { * @param contentType * @param data */ - private static processFields(obj: IContentType | Field, titleValue: string, data: any) { + private static async processFields(obj: IContentType | Field, titleValue: string, data: any, filePath: string) { if (obj.fields) { const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string; @@ -561,19 +561,19 @@ export class ContentType { if (field.name === "title") { if (field.default) { data[field.name] = processKnownPlaceholders(field.default, titleValue, dateFormat); - data[field.name] = ArticleHelper.processCustomPlaceholders(data[field.name], titleValue); + data[field.name] = await ArticleHelper.processCustomPlaceholders(data[field.name], titleValue, filePath); } else { data[field.name] = titleValue; } } else { if (field.type === "fields") { - data[field.name] = this.processFields(field, titleValue, {}); + data[field.name] = await this.processFields(field, titleValue, {}, filePath); } else { const defaultValue = field.default; if (typeof defaultValue === "string") { data[field.name] = processKnownPlaceholders(defaultValue, titleValue, dateFormat); - data[field.name] = ArticleHelper.processCustomPlaceholders(data[field.name], titleValue); + data[field.name] = await ArticleHelper.processCustomPlaceholders(data[field.name], titleValue, filePath); } else { data[field.name] = typeof defaultValue !== "undefined" ? defaultValue : ""; } diff --git a/src/helpers/CustomScript.ts b/src/helpers/CustomScript.ts index ae0989fa..ee4df28f 100644 --- a/src/helpers/CustomScript.ts +++ b/src/helpers/CustomScript.ts @@ -246,7 +246,7 @@ export class CustomScript { * @param args * @returns */ - private static async executeScript(script: ICustomScript, wsPath: string, args: string): Promise { + public static async executeScript(script: ICustomScript, wsPath: string, args: string): Promise { return new Promise((resolve, reject) => { // Check the command to use @@ -264,6 +264,11 @@ export class CustomScript { reject(error.message); } + if (stdout && stdout.endsWith(`\n`)) { + // Remove empty line at the end of the string + stdout = stdout.slice(0, -1); + } + resolve(stdout); }); }); diff --git a/src/listeners/panel/DataListener.ts b/src/listeners/panel/DataListener.ts index aa056ed9..aa2ab62e 100644 --- a/src/listeners/panel/DataListener.ts +++ b/src/listeners/panel/DataListener.ts @@ -322,11 +322,18 @@ export class DataListener extends BaseListener { } } - private static updatePlaceholder(field: string, value: string, title: string) { + /** + * Update the placeholder + * @param field + * @param value + * @param title + */ + private static async updatePlaceholder(field: string, value: string, title: string) { if (field && value) { + const crntFile = window.activeTextEditor?.document; const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string; value = processKnownPlaceholders(value, title || "", dateFormat); - value = ArticleHelper.processCustomPlaceholders(value, title || ""); + value = await ArticleHelper.processCustomPlaceholders(value, title || "", crntFile?.uri.fsPath || ""); } this.sendMsg(Command.updatePlaceholder, { field, value }); diff --git a/src/models/CustomPlaceholder.ts b/src/models/CustomPlaceholder.ts new file mode 100644 index 00000000..b95d783b --- /dev/null +++ b/src/models/CustomPlaceholder.ts @@ -0,0 +1,6 @@ +export interface CustomPlaceholder { + id: string; + value?: string; + script?: string; + command?: string +} \ No newline at end of file diff --git a/src/models/PanelSettings.ts b/src/models/PanelSettings.ts index 51439951..7366d567 100644 --- a/src/models/PanelSettings.ts +++ b/src/models/PanelSettings.ts @@ -118,7 +118,7 @@ export interface CustomScript { output?: "notification" | "editor"; outputType?: string; type?: ScriptType; - command?: CommandType; + command?: CommandType | string; } export interface PreviewSettings { diff --git a/src/models/index.ts b/src/models/index.ts index 65b60c25..51c13234 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,6 +1,7 @@ export * from './BlockFieldData'; export * from './Choice'; export * from './ContentFolder'; +export * from './CustomPlaceholder'; export * from './CustomTaxonomyData'; export * from './DashboardData'; export * from './DataFile';