From dfd01deb0e8e68c09a7701dc655e556224650762 Mon Sep 17 00:00:00 2001 From: badsketch Date: Fri, 1 Sep 2023 00:32:39 -0400 Subject: [PATCH] Integrate explicit type modifiers to wikilink syntax --- .../features/preview/wikilink-embed.spec.ts | 76 +++++++++++++++++++ .../features/preview/wikilink-embed.test.ts | 54 ++++++++++++- .../src/features/preview/wikilink-embed.ts | 20 ++++- 3 files changed, 145 insertions(+), 5 deletions(-) diff --git a/packages/foam-vscode/src/features/preview/wikilink-embed.spec.ts b/packages/foam-vscode/src/features/preview/wikilink-embed.spec.ts index fe36b379e..40918b067 100644 --- a/packages/foam-vscode/src/features/preview/wikilink-embed.spec.ts +++ b/packages/foam-vscode/src/features/preview/wikilink-embed.spec.ts @@ -276,6 +276,82 @@ This is the first subsection of note E`, await deleteFile(note); }); + it('should allow a note embedding type to be overridden if a modifier is passed in', async () => { + const note = await createFile( + ` +# Section 1 +This is the first section of note E + +# Section 2 +This is the second section of note E + +# Section 3 +This is the third section of note E + `, + ['note-e.md'] + ); + const parser = createMarkdownParser([]); + const ws = new FoamWorkspace().set(parser.parse(note.uri, note.content)); + const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser); + + await withModifiedFoamConfiguration( + CONFIG_EMBED_NOTE_TYPE, + 'full-inline', + () => { + expect( + md.render(`This is the root node. + + content![[note-e#Section 2]] + + full![[note-e#Section 3]]`) + ).toMatch( + `

This is the root node.

+

This is the second section of note E

+

+

Section 3

+

This is the third section of note E

+

+` + ); + } + ); + + await deleteFile(note); + }); + + it('should allow a note embedding type to be overridden if two modifiers are passed in', async () => { + const note = await createFile( + ` +# Section 1 +This is the first section of note E + +# Section 2 +This is the second section of note E + `, + ['note-e.md'] + ); + const parser = createMarkdownParser([]); + const ws = new FoamWorkspace().set(parser.parse(note.uri, note.content)); + const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser); + + await withModifiedFoamConfiguration( + CONFIG_EMBED_NOTE_TYPE, + 'full-inline', + () => { + const res = md.render(`This is the root node. + +content-card![[note-e#Section 2]]`); + + expect(res).toContain('This is the root node'); + expect(res).toContain('embed-container-note'); + expect(res).toContain('This is the second section of note E'); + expect(res).not.toContain('Section 2'); + } + ); + + await deleteFile(note); + }); + it('should fallback to the bare text when the note is not found', () => { const md = markdownItWikilinkEmbed( MarkdownIt(), diff --git a/packages/foam-vscode/src/features/preview/wikilink-embed.test.ts b/packages/foam-vscode/src/features/preview/wikilink-embed.test.ts index 68edf487d..56202e8a7 100644 --- a/packages/foam-vscode/src/features/preview/wikilink-embed.test.ts +++ b/packages/foam-vscode/src/features/preview/wikilink-embed.test.ts @@ -1,4 +1,8 @@ -import { retrieveNoteConfig } from './wikilink-embed'; +import { + WIKILINK_EMBED_REGEX, + WIKILINK_EMBED_REGEX_GROUPS, + retrieveNoteConfig, +} from './wikilink-embed'; import * as config from '../../services/config'; describe('Wikilink Note Embedding', () => { @@ -6,6 +10,54 @@ describe('Wikilink Note Embedding', () => { jest.clearAllMocks(); }); + describe('Wikilink Parsing', () => { + it('should match a wikilink item including a modifier and wikilink', () => { + // no configuration + expect('![[note-a]]').toMatch(WIKILINK_EMBED_REGEX); + + // one of the configurations + expect('full![[note-a]]').toMatch(WIKILINK_EMBED_REGEX); + expect('content![[note-a]]').toMatch(WIKILINK_EMBED_REGEX); + expect('inline![[note-a]]').toMatch(WIKILINK_EMBED_REGEX); + expect('card![[note-a]]').toMatch(WIKILINK_EMBED_REGEX); + + // any combination of configurations + expect('full-inline![[note-a]]').toMatch(WIKILINK_EMBED_REGEX); + expect('full-card![[note-a]]').toMatch(WIKILINK_EMBED_REGEX); + expect('content-inline![[note-a]]').toMatch(WIKILINK_EMBED_REGEX); + expect('content-card![[note-a]]').toMatch(WIKILINK_EMBED_REGEX); + }); + + it('should only match the wikilink if there are unrecognized keywords', () => { + const match1 = 'random-word![[note-a]]'.match(WIKILINK_EMBED_REGEX); + expect(match1[0]).toEqual('![[note-a]]'); + expect(match1[1]).toEqual('![[note-a]]'); + + const match2 = 'foo![[note-a#section 1]]'.match(WIKILINK_EMBED_REGEX); + expect(match2[0]).toEqual('![[note-a#section 1]]'); + expect(match2[1]).toEqual('![[note-a#section 1]]'); + }); + + it('should group the wikilink into modifier and wikilink', () => { + const match1 = 'content![[note-a]]'.match(WIKILINK_EMBED_REGEX_GROUPS); + expect(match1[0]).toEqual('content![[note-a]]'); + expect(match1[1]).toEqual('content'); + expect(match1[2]).toEqual('note-a'); + + const match2 = 'full-inline![[note-a#section 1]]'.match( + WIKILINK_EMBED_REGEX_GROUPS + ); + expect(match2[0]).toEqual('full-inline![[note-a#section 1]]'); + expect(match2[1]).toEqual('full-inline'); + expect(match2[2]).toEqual('note-a#section 1'); + + const match3 = '![[note-a#section 1]]'.match(WIKILINK_EMBED_REGEX_GROUPS); + expect(match3[0]).toEqual('![[note-a#section 1]]'); + expect(match3[1]).toEqual(undefined); + expect(match3[2]).toEqual('note-a#section 1'); + }); + }); + describe('Config Parsing', () => { it('should use preview.embedNoteType if an explicit modifier is not passed in', () => { jest diff --git a/packages/foam-vscode/src/features/preview/wikilink-embed.ts b/packages/foam-vscode/src/features/preview/wikilink-embed.ts index 04076c1de..66e5e7913 100644 --- a/packages/foam-vscode/src/features/preview/wikilink-embed.ts +++ b/packages/foam-vscode/src/features/preview/wikilink-embed.ts @@ -14,6 +14,13 @@ import { MarkdownLink } from '../../core/services/markdown-link'; import { Position } from '../../core/model/position'; import { TextEdit } from '../../core/services/text-edit'; +export const WIKILINK_EMBED_REGEX = + /((?:(?:full|content)-(?:inline|card)|full|content|inline|card)?!\[\[[^[\]]+?\]\])/; +// we need another regex because md.use(regex, replace) only permits capturing one group +// so we capture the entire possible wikilink item (ex. content-card![[note]]) using WIKILINK_EMBED_REGEX and then +// use WIKILINK_EMBED_REGEX_GROUPER to parse it into the modifier(content-card) and the wikilink(note) +export const WIKILINK_EMBED_REGEX_GROUPS = + /((?:\w+)|(?:(?:\w+)-(?:\w+)))?!\[\[([^[\]]+?)\]\]/; export const CONFIG_EMBED_NOTE_TYPE = 'preview.embedNoteType'; const refsStack: string[] = []; @@ -24,9 +31,13 @@ export const markdownItWikilinkEmbed = ( ) => { return md.use(markdownItRegex, { name: 'embed-wikilinks', - regex: /!\[\[([^[\]]+?)\]\]/, - replace: (wikilink: string) => { + regex: WIKILINK_EMBED_REGEX, + replace: (wikilinkItem: string) => { try { + const [_, noteEmbedModifier, wikilink] = wikilinkItem.match( + WIKILINK_EMBED_REGEX_GROUPS + ); + const includedNote = workspace.find(wikilink); if (!includedNote) { @@ -49,7 +60,8 @@ export const markdownItWikilinkEmbed = ( switch (includedNote.type) { case 'note': { - const { noteScope, noteStyle } = retrieveNoteConfig(undefined); + const { noteScope, noteStyle } = + retrieveNoteConfig(noteEmbedModifier); const extractor: EmbedNoteExtractor = noteScope === 'full' @@ -88,7 +100,7 @@ Embed for attachments is not supported return html; } catch (e) { Logger.error( - `Error while including [[${wikilink}]] into the current document of the Preview panel`, + `Error while including ${wikilinkItem} into the current document of the Preview panel`, e ); return '';