Skip to content

Commit

Permalink
Feat #879: Content embedding style (#1279)
Browse files Browse the repository at this point in the history
* Add content extractor and enable it as an option for note embed type

* Fix content extractor edgecase where note title has lines above it
  • Loading branch information
badsketch authored Aug 30, 2023
1 parent 3ed2bcd commit eee364d
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 15 deletions.
8 changes: 6 additions & 2 deletions packages/foam-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -578,11 +578,15 @@
"default": "full-card",
"enum": [
"full-inline",
"full-card"
"full-card",
"content-inline",
"content-card"
],
"enumDescriptions": [
"Include the section with title and style inline",
"Include the section with title and style it within a container"
"Include the section with title and style it within a container",
"Include the section without title and style inline",
"Include the section without title and style it within a container"
]
},
"foam.graph.titleMaxLength": {
Expand Down
197 changes: 186 additions & 11 deletions packages/foam-vscode/src/features/preview/wikilink-embed.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
const parser = createMarkdownParser();

describe('Displaying included notes in preview', () => {
it('should render an included note in flat mode', async () => {
it('should render an included note in full inline mode', async () => {
const note = await createFile('This is the text of note A', [
'preview',
'note-a.md',
Expand Down Expand Up @@ -47,7 +47,7 @@ describe('Displaying included notes in preview', () => {
await deleteFile(note);
});

it('should render an included note in container mode', async () => {
it('should render an included note in full card mode', async () => {
const note = await createFile('This is the text of note A', [
'preview',
'note-a.md',
Expand Down Expand Up @@ -75,7 +75,7 @@ describe('Displaying included notes in preview', () => {
await deleteFile(note);
});

it('should render an included section', async () => {
it('should render an included section in full inline mode', async () => {
// here we use createFile as the test note doesn't fill in
// all the metadata we need
const note = await createFile(
Expand Down Expand Up @@ -121,7 +121,7 @@ This is the third section of note E
await deleteFile(note);
});

it('should render an included section in container mode', async () => {
it('should render an included section in full card mode', async () => {
const note = await createFile(
`
# Section 1
Expand Down Expand Up @@ -163,6 +163,168 @@ This is the third section of note E
await deleteFile(note);
});

it('should not render the title of a note in content inline mode', async () => {
const note = await createFile(
`
# Title
## Section 1
This is the first section of note E`,
['note-e.md']
);
const parser = createMarkdownParser([]);
const ws = new FoamWorkspace().set(parser.parse(note.uri, note.content));

await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_IN_CONTAINER,
null,
async () => {
await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_TYPE,
'content-inline',
() => {
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);

expect(
md.render(`This is the root node.
![[note-e]]`)
).toMatch(
`<p>This is the root node.</p>
<p><h2>Section 1</h2>
<p>This is the first section of note E</p>
</p>`
);
}
);
}
);

await deleteFile(note);
});

it('should not render the title of a note in content card mode', async () => {
const note = await createFile(
`# Title
## Section 1
This is the first section of note E
`,
['note-e.md']
);
const parser = createMarkdownParser([]);
const ws = new FoamWorkspace().set(parser.parse(note.uri, note.content));

await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_IN_CONTAINER,
null,
async () => {
await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_TYPE,
'content-card',
() => {
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);

const res = md.render(`This is the root node. ![[note-e.md]]`);

expect(res).toContain('This is the root node');
expect(res).toContain('embed-container-note');
expect(res).toContain('Section 1');
expect(res).toContain('This is the first section of note E');
expect(res).not.toContain('Title');
}
);
}
);

await deleteFile(note);
});

it('should not render the section title, but still render subsection titles in content inline mode', async () => {
const note = await createFile(
`# Title
## Section 1
This is the first section of note E
### Subsection a
This is the first subsection of note E
`,
['note-e.md']
);
const parser = createMarkdownParser([]);
const ws = new FoamWorkspace().set(parser.parse(note.uri, note.content));

await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_IN_CONTAINER,
null,
async () => {
await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_TYPE,
'content-inline',
() => {
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);

expect(
md.render(`This is the root node.
![[note-e#Section 1]]`)
).toMatch(
`<p>This is the root node.</p>
<p><p>This is the first section of note E</p>
<h3>Subsection a</h3>
<p>This is the first subsection of note E</p>
</p>`
);
}
);
}
);

await deleteFile(note);
});

it('should not render the subsection title in content mode if you link to it and regardless of its level', async () => {
const note = await createFile(
`# Title
## Section 1
This is the first section of note E
### Subsection a
This is the first subsection of note E`,
['note-e.md']
);
const parser = createMarkdownParser([]);
const ws = new FoamWorkspace().set(parser.parse(note.uri, note.content));

await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_IN_CONTAINER,
null,
async () => {
await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_TYPE,
'content-inline',
() => {
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);

expect(
md.render(`This is the root node.
![[note-e#Subsection a]]`)
).toMatch(
`<p>This is the root node.</p>
<p><p>This is the first subsection of note E</p>
</p>`
);
}
);
}
);

await deleteFile(note);
});

it('should fallback to the bare text when the note is not found', () => {
const md = markdownItWikilinkEmbed(
MarkdownIt(),
Expand All @@ -187,14 +349,27 @@ This is the third section of note E
const ws = new FoamWorkspace()
.set(parser.parse(noteA.uri, noteA.content))
.set(parser.parse(noteB.uri, noteB.content));
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);
const res = md.render(noteBText);

expect(res).toContain('This is the text of note B which includes');
expect(res).toContain('This is the text of note A which includes');
expect(res).toContain('Cyclic link detected for wikilink');
await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_IN_CONTAINER,
null,
async () => {
await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_TYPE,
'full-card',
() => {
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);
const res = md.render(noteBText);

expect(res).toContain('This is the text of note B which includes');
expect(res).toContain('This is the text of note A which includes');
expect(res).toContain('Cyclic link detected for wikilink');
}
);
}
);

deleteFile(noteA);
deleteFile(noteB);
await deleteFile(noteA);
await deleteFile(noteB);
});
});
35 changes: 33 additions & 2 deletions packages/foam-vscode/src/features/preview/wikilink-embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ export const markdownItWikilinkEmbed = (

switch (includedNote.type) {
case 'note': {
const { noteScope: _, noteStyle } = retrieveNoteConfig();
const { noteScope, noteStyle } = retrieveNoteConfig();

const extractor: EmbedNoteExtractor = fullExtractor;
const extractor: EmbedNoteExtractor =
noteScope === 'full'
? fullExtractor
: noteScope === 'content'
? contentExtractor
: fullExtractor;

const formatter: EmbedNoteFormatter =
noteStyle === 'card'
Expand Down Expand Up @@ -162,6 +167,32 @@ function fullExtractor(
return noteText;
}

function contentExtractor(
note: Resource,
parser: ResourceParser,
workspace: FoamWorkspace
): string {
let noteText = readFileSync(note.uri.toFsPath()).toString();
let section = Resource.findSection(note, note.uri.fragment);
if (!note.uri.fragment) {
// if there's no fragment(section), the wikilink is linking to the entire note,
// in which case we need to remove the title. We could just use rows.shift()
// but should the note start with blank lines, it will only remove the first blank line
// leaving the title
// A better way is to find where the actual title starts by assuming it's at section[0]
// then we treat it as the same case as link to a section
section = note.sections.length ? note.sections[0] : null;
}
let rows = noteText.split('\n');
if (isSome(section)) {
rows = rows.slice(section.range.start.line, section.range.end.line);
}
rows.shift();
noteText = rows.join('\n');
noteText = withLinksRelativeToWorkspaceRoot(noteText, parser, workspace);
return noteText;
}

/**
* A type of function that renders note content with the desired style in html
*/
Expand Down

0 comments on commit eee364d

Please sign in to comment.