Skip to content

Commit a3b375e

Browse files
Fix wikilink embed (#1430)
* wikilink embed: fixed bug in resolution, and extracted getNoteContent function Bug was due to missing base note when resolving links in `withLinksRelativeToWorkspaceRoot`. Also updated link to be rendered in HTML as absolute * Improved embed reporting when running in web mode * Fixed test (by not preloading document selectors)
1 parent c840070 commit a3b375e

File tree

9 files changed

+154
-79
lines changed

9 files changed

+154
-79
lines changed

packages/foam-vscode/src/features/commands/update-wikilinks.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
workspace,
1212
Position,
1313
} from 'vscode';
14-
import { isMdEditor, mdDocSelector } from '../../services/editor';
14+
import { isMdEditor, getFoamDocSelectors } from '../../services/editor';
1515
import { Foam } from '../../core/model/foam';
1616
import { FoamWorkspace } from '../../core/model/workspace';
1717
import {
@@ -48,7 +48,7 @@ export default async function activate(
4848
);
4949
}),
5050
languages.registerCodeLensProvider(
51-
mdDocSelector,
51+
getFoamDocSelectors(),
5252
new WikilinkReferenceCodeLensProvider(
5353
foam.workspace,
5454
foam.services.parser

packages/foam-vscode/src/features/hover-provider.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { OPEN_COMMAND } from './commands/open-resource';
1414
import { CREATE_NOTE_COMMAND } from './commands/create-note';
1515
import { commandAsURI } from '../utils/commands';
1616
import { Location } from '../core/model/location';
17-
import { getNoteTooltip, mdDocSelector } from '../services/editor';
17+
import { getNoteTooltip, getFoamDocSelectors } from '../services/editor';
1818
import { isSome } from '../core/utils';
1919

2020
export const CONFIG_KEY = 'links.hover.enable';
@@ -31,7 +31,7 @@ export default async function activate(
3131
context.subscriptions.push(
3232
isHoverEnabled,
3333
vscode.languages.registerHoverProvider(
34-
mdDocSelector,
34+
getFoamDocSelectors(),
3535
new HoverProvider(
3636
isHoverEnabled,
3737
foam.workspace,

packages/foam-vscode/src/features/link-completion.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { URI } from '../core/model/uri';
66
import { FoamWorkspace } from '../core/model/workspace';
77
import { getFoamVsCodeConfig } from '../services/config';
88
import { fromVsCodeUri, toVsCodeUri } from '../utils/vsc-utils';
9-
import { getNoteTooltip, mdDocSelector } from '../services/editor';
9+
import { getNoteTooltip, getFoamDocSelectors } from '../services/editor';
1010

1111
export const aliasCommitCharacters = ['#'];
1212
export const linkCommitCharacters = ['#', '|'];
@@ -27,12 +27,12 @@ export default async function activate(
2727
const foam = await foamPromise;
2828
context.subscriptions.push(
2929
vscode.languages.registerCompletionItemProvider(
30-
mdDocSelector,
30+
getFoamDocSelectors(),
3131
new WikilinkCompletionProvider(foam.workspace, foam.graph),
3232
'['
3333
),
3434
vscode.languages.registerCompletionItemProvider(
35-
mdDocSelector,
35+
getFoamDocSelectors(),
3636
new SectionCompletionProvider(foam.workspace),
3737
'#'
3838
),

packages/foam-vscode/src/features/navigation-provider.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Position } from '../core/model/position';
1010
import { CREATE_NOTE_COMMAND } from './commands/create-note';
1111
import { commandAsURI } from '../utils/commands';
1212
import { Location } from '../core/model/location';
13-
import { mdDocSelector } from '../services/editor';
13+
import { getFoamDocSelectors } from '../services/editor';
1414

1515
export default async function activate(
1616
context: vscode.ExtensionContext,
@@ -26,15 +26,15 @@ export default async function activate(
2626

2727
context.subscriptions.push(
2828
vscode.languages.registerDefinitionProvider(
29-
mdDocSelector,
29+
getFoamDocSelectors(),
3030
navigationProvider
3131
),
3232
vscode.languages.registerDocumentLinkProvider(
33-
mdDocSelector,
33+
getFoamDocSelectors(),
3434
navigationProvider
3535
),
3636
vscode.languages.registerReferenceProvider(
37-
mdDocSelector,
37+
getFoamDocSelectors(),
3838
navigationProvider
3939
)
4040
);

packages/foam-vscode/src/features/preview/wikilink-embed-web-extension.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ export const markdownItWikilinkEmbed = (
1717
regex: WIKILINK_EMBED_REGEX,
1818
replace: (wikilinkItem: string) => {
1919
return `
20-
<div style="padding: 0.25em; margin: 1.5em 0; text-align: center; border: 1px solid var(--vscode-editorLineNumber-foreground);">
21-
Embeds are not supported in web extension: <br/> ${wikilinkItem}
22-
</div>`;
20+
<div class="foam-embed-not-supported-warning">
21+
Embed not supported in web mode: ${wikilinkItem}
22+
</div>
23+
`;
2324
},
2425
});
2526
};

packages/foam-vscode/src/features/preview/wikilink-embed.ts

+105-55
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@ import { Resource, ResourceParser } from '../../core/model/note';
1010
import { getFoamVsCodeConfig } from '../../services/config';
1111
import { fromVsCodeUri, toVsCodeUri } from '../../utils/vsc-utils';
1212
import { MarkdownLink } from '../../core/services/markdown-link';
13+
import { URI } from '../../core/model/uri';
1314
import { Position } from '../../core/model/position';
1415
import { TextEdit } from '../../core/services/text-edit';
1516
import { isNone, isSome } from '../../core/utils';
17+
import {
18+
asAbsoluteWorkspaceUri,
19+
isVirtualWorkspace,
20+
} from '../../services/editor';
1621

1722
export const WIKILINK_EMBED_REGEX =
1823
/((?:(?:full|content)-(?:inline|card)|full|content|inline|card)?!\[\[[^[\]]+?\]\])/;
@@ -22,7 +27,7 @@ export const WIKILINK_EMBED_REGEX =
2227
export const WIKILINK_EMBED_REGEX_GROUPS =
2328
/((?:\w+)|(?:(?:\w+)-(?:\w+)))?!\[\[([^[\]]+?)\]\]/;
2429
export const CONFIG_EMBED_NOTE_TYPE = 'preview.embedNoteType';
25-
const refsStack: string[] = [];
30+
let refsStack: string[] = [];
2631

2732
export const markdownItWikilinkEmbed = (
2833
md: markdownit,
@@ -38,6 +43,14 @@ export const markdownItWikilinkEmbed = (
3843
WIKILINK_EMBED_REGEX_GROUPS
3944
);
4045

46+
if (isVirtualWorkspace()) {
47+
return `
48+
<div class="foam-embed-not-supported-warning">
49+
Embed not supported in virtual workspace: ![[${wikilink}]]
50+
</div>
51+
`;
52+
}
53+
4154
const includedNote = workspace.find(wikilink);
4255

4356
if (!includedNote) {
@@ -48,56 +61,31 @@ export const markdownItWikilinkEmbed = (
4861
includedNote.uri.path.toLocaleLowerCase()
4962
);
5063

51-
if (!cyclicLinkDetected) {
52-
refsStack.push(includedNote.uri.path.toLocaleLowerCase());
53-
}
54-
5564
if (cyclicLinkDetected) {
56-
return `<div class="foam-cyclic-link-warning">Cyclic link detected for wikilink: ${wikilink}</div>`;
57-
}
58-
let content = `Embed for [[${wikilink}]]`;
59-
let html: string;
60-
61-
switch (includedNote.type) {
62-
case 'note': {
63-
const { noteScope, noteStyle } =
64-
retrieveNoteConfig(noteEmbedModifier);
65-
66-
const extractor: EmbedNoteExtractor =
67-
noteScope === 'full'
68-
? fullExtractor
69-
: noteScope === 'content'
70-
? contentExtractor
71-
: fullExtractor;
72-
73-
const formatter: EmbedNoteFormatter =
74-
noteStyle === 'card'
75-
? cardFormatter
76-
: noteStyle === 'inline'
77-
? inlineFormatter
78-
: cardFormatter;
79-
80-
content = extractor(includedNote, parser, workspace);
81-
html = formatter(content, md);
82-
break;
83-
}
84-
case 'attachment':
85-
content = `
86-
<div class="embed-container-attachment">
87-
${md.renderInline('[[' + wikilink + ']]')}<br/>
88-
Embed for attachments is not supported
89-
</div>`;
90-
html = md.render(content);
91-
break;
92-
case 'image':
93-
content = `<div class="embed-container-image">${md.render(
94-
`![](${md.normalizeLink(includedNote.uri.path)})`
95-
)}</div>`;
96-
html = md.render(content);
97-
break;
65+
return `
66+
<div class="foam-cyclic-link-warning">
67+
Cyclic link detected for wikilink: ${wikilink}
68+
<div class="foam-cyclic-link-warning__stack">
69+
Link sequence:
70+
<ul>
71+
${refsStack.map(ref => `<li>${ref}</li>`).join('')}
72+
</ul>
73+
</div>
74+
</div>
75+
`;
9876
}
77+
78+
refsStack.push(includedNote.uri.path.toLocaleLowerCase());
79+
80+
const content = getNoteContent(
81+
includedNote,
82+
noteEmbedModifier,
83+
parser,
84+
workspace,
85+
md
86+
);
9987
refsStack.pop();
100-
return html;
88+
return content;
10189
} catch (e) {
10290
Logger.error(
10391
`Error while including ${wikilinkItem} into the current document of the Preview panel`,
@@ -109,27 +97,79 @@ Embed for attachments is not supported
10997
});
11098
};
11199

100+
function getNoteContent(
101+
includedNote: Resource,
102+
noteEmbedModifier: string | undefined,
103+
parser: ResourceParser,
104+
workspace: FoamWorkspace,
105+
md: markdownit
106+
): string {
107+
let content = `Embed for [[${includedNote.uri.path}]]`;
108+
let html: string;
109+
110+
switch (includedNote.type) {
111+
case 'note': {
112+
const { noteScope, noteStyle } = retrieveNoteConfig(noteEmbedModifier);
113+
114+
const extractor: EmbedNoteExtractor =
115+
noteScope === 'full'
116+
? fullExtractor
117+
: noteScope === 'content'
118+
? contentExtractor
119+
: fullExtractor;
120+
121+
const formatter: EmbedNoteFormatter =
122+
noteStyle === 'card'
123+
? cardFormatter
124+
: noteStyle === 'inline'
125+
? inlineFormatter
126+
: cardFormatter;
127+
128+
content = extractor(includedNote, parser, workspace);
129+
html = formatter(content, md);
130+
break;
131+
}
132+
case 'attachment':
133+
content = `
134+
<div class="embed-container-attachment">
135+
${md.renderInline('[[' + includedNote.uri.path + ']]')}<br/>
136+
Embed for attachments is not supported
137+
</div>`;
138+
html = md.render(content);
139+
break;
140+
case 'image':
141+
content = `<div class="embed-container-image">${md.render(
142+
`![](${md.normalizeLink(includedNote.uri.path)})`
143+
)}</div>`;
144+
html = md.render(content);
145+
break;
146+
default:
147+
html = content;
148+
}
149+
150+
return html;
151+
}
152+
112153
function withLinksRelativeToWorkspaceRoot(
154+
noteUri: URI,
113155
noteText: string,
114156
parser: ResourceParser,
115157
workspace: FoamWorkspace
116-
) {
158+
): string {
117159
const note = parser.parse(
118160
fromVsCodeUri(vsWorkspace.workspaceFolders[0].uri),
119161
noteText
120162
);
121163
const edits = note.links
122164
.map(link => {
123165
const info = MarkdownLink.analyzeLink(link);
124-
const resource = workspace.find(info.target);
166+
const resource = workspace.find(info.target, noteUri);
125167
// embedded notes that aren't created are still collected
126168
// return null so it can be filtered in the next step
127169
if (isNone(resource)) {
128170
return null;
129171
}
130-
const pathFromRoot = vsWorkspace.asRelativePath(
131-
toVsCodeUri(resource.uri)
132-
);
172+
const pathFromRoot = asAbsoluteWorkspaceUri(resource.uri).path;
133173
return MarkdownLink.createUpdateLinkEdit(link, {
134174
target: pathFromRoot,
135175
});
@@ -185,7 +225,12 @@ function fullExtractor(
185225
.slice(section.range.start.line, section.range.end.line)
186226
.join('\n');
187227
}
188-
noteText = withLinksRelativeToWorkspaceRoot(noteText, parser, workspace);
228+
noteText = withLinksRelativeToWorkspaceRoot(
229+
note.uri,
230+
noteText,
231+
parser,
232+
workspace
233+
);
189234
return noteText;
190235
}
191236

@@ -211,7 +256,12 @@ function contentExtractor(
211256
}
212257
rows.shift();
213258
noteText = rows.join('\n');
214-
noteText = withLinksRelativeToWorkspaceRoot(noteText, parser, workspace);
259+
noteText = withLinksRelativeToWorkspaceRoot(
260+
note.uri,
261+
noteText,
262+
parser,
263+
workspace
264+
);
215265
return noteText;
216266
}
217267

packages/foam-vscode/src/features/tag-completion.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
22
import { Foam } from '../core/model/foam';
33
import { FoamTags } from '../core/model/tags';
44
import { isInFrontMatter, isOnYAMLKeywordLine } from '../core/utils/md';
5-
import { mdDocSelector } from '../services/editor';
5+
import { getFoamDocSelectors } from '../services/editor';
66

77
// this regex is different from HASHTAG_REGEX in that it does not look for a
88
// #+character. It uses a negative look-ahead for `# `
@@ -17,7 +17,7 @@ export default async function activate(
1717
const foam = await foamPromise;
1818
context.subscriptions.push(
1919
vscode.languages.registerCompletionItemProvider(
20-
mdDocSelector,
20+
getFoamDocSelectors(),
2121
new TagCompletionProvider(foam.tags),
2222
'#'
2323
)

packages/foam-vscode/src/services/editor.ts

+19-8
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,14 @@ export function formatMarkdownTooltip(content: string): MarkdownString {
5454
}
5555

5656
// Generate the document selector dynamically
57-
export const mdDocSelector = getFoamVsCodeConfig<string[]>(
58-
'supportedLanguages',
59-
['markdown']
60-
).flatMap(lang => [
61-
{ language: lang, scheme: 'file' }, // Local files
62-
{ language: lang, scheme: 'vscode-vfs' }, // Remote files
63-
{ language: lang, scheme: 'untitled' }, // Untitled files
64-
]);
57+
export const getFoamDocSelectors = () =>
58+
getFoamVsCodeConfig<string[]>('supportedLanguages', ['markdown']).flatMap(
59+
lang => [
60+
{ language: lang, scheme: 'file' }, // Local files
61+
{ language: lang, scheme: 'vscode-vfs' }, // Remote files
62+
{ language: lang, scheme: 'untitled' }, // Untitled files
63+
]
64+
);
6565

6666
// Check if the editor's document is a supported language
6767
export function isMdEditor(editor: TextEditor): boolean {
@@ -76,6 +76,17 @@ export function isMdEditor(editor: TextEditor): boolean {
7676
);
7777
}
7878

79+
/**
80+
* Check if the workspace contains remote or virtual file system folders.
81+
* @returns True if the workspace contains remote or virtual file system folders, false otherwise.
82+
*/
83+
export function isVirtualWorkspace(): boolean {
84+
return workspace.workspaceFolders.some(folder => {
85+
const scheme = folder.uri.scheme;
86+
return scheme === 'vscode-remote' || scheme === 'vscode-vfs';
87+
});
88+
}
89+
7990
export function findSelectionContent(): SelectionInfo | undefined {
8091
const editor = window.activeTextEditor;
8192
if (editor === undefined) {

0 commit comments

Comments
 (0)