diff --git a/extensions/markdown-language-features/src/features/pathCompletions.ts b/extensions/markdown-language-features/src/features/pathCompletions.ts index 32b3142e10a..c8e3a4f4ec9 100644 --- a/extensions/markdown-language-features/src/features/pathCompletions.ts +++ b/extensions/markdown-language-features/src/features/pathCompletions.ts @@ -63,6 +63,14 @@ interface CompletionContext { readonly anchorInfo?: AnchorContext; } +function tryDecodeUriComponent(str: string): string { + try { + return decodeURIComponent(str); + } catch { + return str; + } +} + export class PathCompletionProvider implements vscode.CompletionItemProvider { public static register(selector: vscode.DocumentSelector, engine: MarkdownEngine): vscode.Disposable { @@ -157,7 +165,7 @@ export class PathCompletionProvider implements vscode.CompletionItemProvider { const suffix = lineSuffixText.match(/^[^\)\s]*/); return { kind: CompletionContextKind.Link, - linkPrefix: prefix, + linkPrefix: tryDecodeUriComponent(prefix), linkTextStartPosition: position.translate({ characterDelta: -prefix.length }), linkSuffix: suffix ? suffix[0] : '', anchorInfo: this.getAnchorContext(prefix), @@ -174,7 +182,7 @@ export class PathCompletionProvider implements vscode.CompletionItemProvider { const suffix = lineSuffixText.match(/^[^\s]*/); return { kind: CompletionContextKind.LinkDefinition, - linkPrefix: prefix, + linkPrefix: tryDecodeUriComponent(prefix), linkTextStartPosition: position.translate({ characterDelta: -prefix.length }), linkSuffix: suffix ? suffix[0] : '', anchorInfo: this.getAnchorContext(prefix), diff --git a/extensions/markdown-language-features/src/test/pathCompletion.test.ts b/extensions/markdown-language-features/src/test/pathCompletion.test.ts index e12cf575b92..1fba12d7b64 100644 --- a/extensions/markdown-language-features/src/test/pathCompletion.test.ts +++ b/extensions/markdown-language-features/src/test/pathCompletion.test.ts @@ -151,4 +151,20 @@ suite('Markdown path completion provider', () => { assert.ok(completions.some(x => x.insertText === 'file%20with%20space.md'), 'Has encoded path completion'); }); + + test('Should complete paths for path with encoded spaces', async () => { + const completions = await getCompletionsAtCursor(workspaceFile('new.md'), joinLines( + `[](./sub%20with%20space/${CURSOR})` + )); + + assert.ok(completions.some(x => x.insertText === 'file.md'), 'Has file from space'); + }); + + test('Should complete definition path for path with encoded spaces', async () => { + const completions = await getCompletionsAtCursor(workspaceFile('new.md'), joinLines( + `[def]: ./sub%20with%20space/${CURSOR}` + )); + + assert.ok(completions.some(x => x.insertText === 'file.md'), 'Has file from space'); + }); }); diff --git a/extensions/markdown-language-features/test-workspace/sub with space/file.md b/extensions/markdown-language-features/test-workspace/sub with space/file.md new file mode 100644 index 00000000000..6f83f34efff --- /dev/null +++ b/extensions/markdown-language-features/test-workspace/sub with space/file.md @@ -0,0 +1 @@ +# header