mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-23 01:58:53 +01:00
@@ -18,6 +18,21 @@ enum CompletionContextKind {
|
||||
LinkDefinition, // []: | // TODO: not implemented
|
||||
}
|
||||
|
||||
interface AnchorContext {
|
||||
/**
|
||||
* Link text before the `#`.
|
||||
*
|
||||
* For `[text](xy#z|abc)` this is `xy`.
|
||||
*/
|
||||
readonly beforeAnchor: string;
|
||||
/**
|
||||
* Text of the anchor before the current position.
|
||||
*
|
||||
* For `[text](xy#z|abc)` this is `z`.
|
||||
*/
|
||||
readonly anchorPrefix: string;
|
||||
}
|
||||
|
||||
interface CompletionContext {
|
||||
readonly kind: CompletionContextKind;
|
||||
|
||||
@@ -45,21 +60,7 @@ interface CompletionContext {
|
||||
/**
|
||||
* Info if the link looks like it is for an anchor: `[](#header)`
|
||||
*/
|
||||
readonly anchorInfo?: {
|
||||
/**
|
||||
* Link text before the `#`.
|
||||
*
|
||||
* For `[text](xy#z|abc)` this is `xy`.
|
||||
*/
|
||||
readonly beforeAnchor: string;
|
||||
|
||||
/**
|
||||
* Text of the anchor before the current position.
|
||||
*
|
||||
* For `[text](xy#z|abc)` this is `z`.
|
||||
*/
|
||||
readonly anchorPrefix: string;
|
||||
}
|
||||
readonly anchorInfo?: AnchorContext
|
||||
}
|
||||
|
||||
export class PathCompletionProvider implements vscode.CompletionItemProvider {
|
||||
@@ -87,10 +88,7 @@ export class PathCompletionProvider implements vscode.CompletionItemProvider {
|
||||
return Array.from(this.provideReferenceSuggestions(document, position, context));
|
||||
}
|
||||
|
||||
case CompletionContextKind.LinkDefinition: {
|
||||
return [];
|
||||
}
|
||||
|
||||
case CompletionContextKind.LinkDefinition:
|
||||
case CompletionContextKind.Link: {
|
||||
const items: vscode.CompletionItem[] = [];
|
||||
|
||||
@@ -140,6 +138,9 @@ export class PathCompletionProvider implements vscode.CompletionItemProvider {
|
||||
/// [...][...|
|
||||
private readonly referenceLinkStartPattern = /\[([^\]]*?)\]\[\s*([^\s\(\)]*)$/;
|
||||
|
||||
/// [id]: |
|
||||
private readonly definitionPattern = /^\s*\[[\w\-]+\]:\s*([^\s]*)$/m;
|
||||
|
||||
private getPathCompletionContext(document: vscode.TextDocument, position: vscode.Position): CompletionContext | undefined {
|
||||
const line = document.lineAt(position.line).text;
|
||||
|
||||
@@ -149,25 +150,34 @@ export class PathCompletionProvider implements vscode.CompletionItemProvider {
|
||||
const linkPrefixMatch = linePrefixText.match(this.linkStartPattern);
|
||||
if (linkPrefixMatch) {
|
||||
const prefix = linkPrefixMatch[2];
|
||||
if (/^\s*[\w\d\-]+:/.test(prefix)) { // Check if this looks like a 'http:' style uri
|
||||
if (this.refLooksLikeUrl(prefix)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const anchorMatch = prefix.match(/^(.*)#([\w\d\-]*)$/);
|
||||
|
||||
const suffix = lineSuffixText.match(/^[^\)\s]*/);
|
||||
|
||||
return {
|
||||
kind: CompletionContextKind.Link,
|
||||
linkPrefix: prefix,
|
||||
linkTextStartPosition: position.translate({ characterDelta: -prefix.length }),
|
||||
|
||||
linkSuffix: suffix ? suffix[0] : '',
|
||||
anchorInfo: this.getAnchorContext(prefix),
|
||||
};
|
||||
}
|
||||
|
||||
anchorInfo: anchorMatch ? {
|
||||
beforeAnchor: anchorMatch[1],
|
||||
anchorPrefix: anchorMatch[2],
|
||||
} : undefined,
|
||||
const definitionLinkPrefixMatch = linePrefixText.match(this.definitionPattern);
|
||||
if (definitionLinkPrefixMatch) {
|
||||
const prefix = definitionLinkPrefixMatch[1];
|
||||
if (this.refLooksLikeUrl(prefix)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const suffix = lineSuffixText.match(/^[^\s]*/);
|
||||
return {
|
||||
kind: CompletionContextKind.LinkDefinition,
|
||||
linkPrefix: prefix,
|
||||
linkTextStartPosition: position.translate({ characterDelta: -prefix.length }),
|
||||
linkSuffix: suffix ? suffix[0] : '',
|
||||
anchorInfo: this.getAnchorContext(prefix),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -179,7 +189,6 @@ export class PathCompletionProvider implements vscode.CompletionItemProvider {
|
||||
kind: CompletionContextKind.ReferenceLink,
|
||||
linkPrefix: prefix,
|
||||
linkTextStartPosition: position.translate({ characterDelta: -prefix.length }),
|
||||
|
||||
linkSuffix: suffix ? suffix[0] : '',
|
||||
};
|
||||
}
|
||||
@@ -187,6 +196,24 @@ export class PathCompletionProvider implements vscode.CompletionItemProvider {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if {@param ref} looks like a 'http:' style url.
|
||||
*/
|
||||
private refLooksLikeUrl(prefix: string): boolean {
|
||||
return /^\s*[\w\d\-]+:/.test(prefix);
|
||||
}
|
||||
|
||||
private getAnchorContext(prefix: string): AnchorContext | undefined {
|
||||
const anchorMatch = prefix.match(/^(.*)#([\w\d\-]*)$/);
|
||||
if (!anchorMatch) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
beforeAnchor: anchorMatch[1],
|
||||
anchorPrefix: anchorMatch[2],
|
||||
};
|
||||
}
|
||||
|
||||
private *provideReferenceSuggestions(document: vscode.TextDocument, position: vscode.Position, context: CompletionContext): Iterable<vscode.CompletionItem> {
|
||||
const insertionRange = new vscode.Range(context.linkTextStartPosition, position);
|
||||
const replacementRange = new vscode.Range(insertionRange.start, position.translate({ characterDelta: context.linkSuffix.length }));
|
||||
|
||||
@@ -121,4 +121,26 @@ suite('Markdown path completion provider', () => {
|
||||
assert.ok(completions.some(x => x.label === 'ref-1'), 'Has ref-1 reference completion');
|
||||
assert.ok(completions.some(x => x.label === 'ref-2'), 'Has ref-2 reference completion');
|
||||
});
|
||||
|
||||
test('Should complete headers in link definitions', async () => {
|
||||
const completions = await getCompletionsAtCursor(workspaceFile('sub', 'new.md'), joinLines(
|
||||
`# a B c`,
|
||||
`# x y Z`,
|
||||
`[ref-1]: ${CURSOR}`,
|
||||
));
|
||||
|
||||
assert.ok(completions.some(x => x.label === '#a-b-c'), 'Has #a-b-c header completion');
|
||||
assert.ok(completions.some(x => x.label === '#x-y-z'), 'Has #x-y-z header completion');
|
||||
});
|
||||
|
||||
test('Should complete relative paths in link definitions', async () => {
|
||||
const completions = await getCompletionsAtCursor(workspaceFile('new.md'), joinLines(
|
||||
`# a B c`,
|
||||
`[ref-1]: ${CURSOR}`,
|
||||
));
|
||||
|
||||
assert.ok(completions.some(x => x.label === 'a.md'), 'Has a.md file completion');
|
||||
assert.ok(completions.some(x => x.label === 'b.md'), 'Has b.md file completion');
|
||||
assert.ok(completions.some(x => x.label === 'sub/'), 'Has sub folder completion');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user