diff --git a/packages/foam-vscode/src/features/create-from-template.ts b/packages/foam-vscode/src/features/create-from-template.ts index 9e6e2f223..d87c60d8b 100644 --- a/packages/foam-vscode/src/features/create-from-template.ts +++ b/packages/foam-vscode/src/features/create-from-template.ts @@ -1,10 +1,11 @@ import { - window, commands, ExtensionContext, - workspace, + QuickPickItem, SnippetString, Uri, + window, + workspace, } from 'vscode'; import * as path from 'path'; import { FoamFeature } from '../types'; @@ -31,7 +32,13 @@ export class UserCancelledOperation extends Error { const knownFoamVariables = new Set(['FOAM_TITLE']); -const defaultTemplateDefaultText: string = '# ${FOAM_TITLE}'; // eslint-disable-line no-template-curly-in-string +const defaultTemplateDefaultText: string = `--- +foam_template: + name: New Note + description: Foam's default new note template +--- +# \${FOAM_TITLE} +`; const defaultTemplateUri = Uri.joinPath(templatesDir, 'new-note.md'); const templateContent = `# \${1:$TM_FILENAME_BASE} @@ -53,9 +60,24 @@ For a full list of features see [the VS Code snippets page](https://code.visuals 2. create a note from this template by running the \`Foam: Create New Note From Template\` command `; -async function getTemplates(): Promise { +async function templateMetadata( + templateUri: Uri +): Promise> { + const contents = await workspace.fs + .readFile(templateUri) + .then(bytes => bytes.toString()); + let templateMetadata: Map, + _templateWithFoamFrontmatterRemoved: string; + [ + templateMetadata, + _templateWithFoamFrontmatterRemoved, + ] = extractFoamTemplateFrontmatterMetadata(contents); + return templateMetadata; +} + +async function getTemplates(): Promise { const templates = await workspace.findFiles('.foam/templates/**.md'); - return templates.map(template => path.basename(template.path)); + return templates; } async function offerToCreateTemplate(): Promise { @@ -155,12 +177,71 @@ export function substituteFoamVariables( return templateText; } +function sortTemplatesMetadata( + t1: Map, + t2: Map +) { + // Sort by name's existence, then name, then path + + if (t1.get('name') === undefined && t2.get('name') !== undefined) { + return 1; + } + + if (t1.get('name') !== undefined && t2.get('name') === undefined) { + return -1; + } + + const pathSortOrder = t1 + .get('templatePath') + .localeCompare(t2.get('templatePath')); + + if (t1.get('name') === undefined && t2.get('name') === undefined) { + return pathSortOrder; + } + + const nameSortOrder = t1.get('name').localeCompare(t2.get('name')); + + return nameSortOrder || pathSortOrder; +} + async function askUserForTemplate() { const templates = await getTemplates(); if (templates.length === 0) { return offerToCreateTemplate(); } - return await window.showQuickPick(templates, { + + const templatesMetadata = ( + await Promise.all( + templates.map(async templateUri => { + const metadata = await templateMetadata(templateUri); + metadata.set('templatePath', path.basename(templateUri.path)); + return metadata; + }) + ) + ).sort(sortTemplatesMetadata); + + const items: QuickPickItem[] = await Promise.all( + templatesMetadata.map(metadata => { + const label = metadata.get('name') || metadata.get('templatePath'); + const description = metadata.get('name') + ? metadata.get('templatePath') + : null; + const detail = metadata.get('description'); + const item = { + label: label, + description: description, + detail: detail, + }; + Object.keys(item).forEach(key => { + if (!item[key]) { + delete item[key]; + } + }); + return item; + }) + ); + + return await window.showQuickPick(items, { placeHolder: 'Select a template to use.', }); } @@ -301,7 +382,9 @@ async function createNoteFromTemplate( if (selectedTemplate === undefined) { return; } - templateFilename = selectedTemplate as string; + templateFilename = + (selectedTemplate as QuickPickItem).description || + (selectedTemplate as QuickPickItem).label; const templateUri = Uri.joinPath(templatesDir, templateFilename); const templateText = await workspace.fs .readFile(templateUri) diff --git a/packages/foam-vscode/src/utils/template-frontmatter-parser.test.ts b/packages/foam-vscode/src/utils/template-frontmatter-parser.test.ts index 67912ce11..5faad2e7d 100644 --- a/packages/foam-vscode/src/utils/template-frontmatter-parser.test.ts +++ b/packages/foam-vscode/src/utils/template-frontmatter-parser.test.ts @@ -65,6 +65,8 @@ foo: bar test('Returns the `foam_template` metadata when it is used in its own frontmatter block', () => { const input = `--- foam_template: + name: My Note Template + description: This is my note template filepath: journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md --- @@ -76,6 +78,8 @@ foam_template: `; const expectedMetadata = new Map(); + expectedMetadata.set('name', 'My Note Template'); + expectedMetadata.set('description', 'This is my note template'); expectedMetadata.set( 'filepath', 'journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md' @@ -89,6 +93,8 @@ foam_template: const input = `--- foam_template: filepath: journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md + description: This is my note template + name: My Note Template --- --- @@ -116,6 +122,8 @@ more_metadata: *info `; const expectedMetadata = new Map(); + expectedMetadata.set('name', 'My Note Template'); + expectedMetadata.set('description', 'This is my note template'); expectedMetadata.set( 'filepath', 'journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md' @@ -129,7 +137,9 @@ more_metadata: *info const input = `--- foo: bar foam_template: + name: My Note Template filepath: journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md + description: This is my note template # A YAML comment metadata: &info title: The Gentlemen @@ -151,6 +161,8 @@ more_metadata: *info # $FOAM_TITLE`; const expectedMetadata = new Map(); + expectedMetadata.set('name', 'My Note Template'); + expectedMetadata.set('description', 'This is my note template'); expectedMetadata.set( 'filepath', 'journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md' @@ -166,7 +178,9 @@ describe('removeFoamMetadata', () => { const input = `--- foo: bar foam_template: &foam_template # A YAML comment + description: This is my note template filepath: journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md # A YAML comment + name: My Note Template # A YAML comment metadata: &info title: The Gentlemen diff --git a/packages/foam-vscode/src/utils/template-frontmatter-parser.ts b/packages/foam-vscode/src/utils/template-frontmatter-parser.ts index 740a73de4..a6e666147 100644 --- a/packages/foam-vscode/src/utils/template-frontmatter-parser.ts +++ b/packages/foam-vscode/src/utils/template-frontmatter-parser.ts @@ -50,5 +50,8 @@ export function extractFoamTemplateFrontmatterMetadata( } export function removeFoamMetadata(contents: string) { - return contents.replace(/^\s*foam_template:.*?\n\s*filepath:.*\n/gm, ''); + return contents.replace( + /^\s*foam_template:.*?\n(?:\s*(?:filepath|name|description):.*\n)+/gm, + '' + ); }