Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts')
-rw-r--r--extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts202
1 files changed, 130 insertions, 72 deletions
diff --git a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts
index 4d474658591..5ee7d1d4b60 100644
--- a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts
+++ b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts
@@ -87,26 +87,26 @@ function getWorkspaceFolder(document: SkinnyTextDocument) {
|| vscode.workspace.workspaceFolders?.[0]?.uri;
}
+interface MdLinkSource {
+ readonly text: string;
+ readonly resource: vscode.Uri;
+ readonly hrefRange: vscode.Range;
+ readonly fragmentRange: vscode.Range | undefined;
+}
+
export interface MdInlineLink {
readonly kind: 'link';
-
+ readonly source: MdLinkSource;
readonly href: LinkHref;
-
- readonly sourceText: string;
- readonly sourceResource: vscode.Uri;
- readonly sourceHrefRange: vscode.Range;
}
export interface MdLinkDefinition {
readonly kind: 'definition';
-
- readonly sourceText: string;
- readonly sourceResource: vscode.Uri;
- readonly sourceHrefRange: vscode.Range;
-
- readonly refRange: vscode.Range;
-
- readonly ref: string;
+ readonly source: MdLinkSource;
+ readonly ref: {
+ readonly range: vscode.Range;
+ readonly text: string;
+ };
readonly href: ExternalHref | InternalHref;
}
@@ -129,15 +129,26 @@ function extractDocumentLink(
return {
kind: 'link',
href: linkTarget,
- sourceText: link,
- sourceResource: document.uri,
- sourceHrefRange: new vscode.Range(linkStart, linkEnd)
+ source: {
+ text: link,
+ resource: document.uri,
+ hrefRange: new vscode.Range(linkStart, linkEnd),
+ fragmentRange: getFragmentRange(link, linkStart, linkEnd),
+ }
};
} catch {
return undefined;
}
}
+function getFragmentRange(text: string, start: vscode.Position, end: vscode.Position): vscode.Range | undefined {
+ const index = text.indexOf('#');
+ if (index < 0) {
+ return undefined;
+ }
+ return new vscode.Range(start.translate({ characterDelta: index + 1 }), end);
+}
+
const angleBracketLinkRe = /^<(.*)>$/;
/**
@@ -160,6 +171,11 @@ const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*
const referenceLinkPattern = /(?:(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]|\[\s*?([^\s\]]*?)\])(?![\:\(])/g;
/**
+ * Matches `<http://example.com>`
+ */
+const autoLinkPattern = /\<(\w+:[^\>\s]+)\>/g;
+
+/**
* Matches `[text]: link`
*/
const definitionPattern = /^([\t ]*\[(?!\^)((?:\\\]|[^\]])+)\]:\s*)([^<]\S*|<[^>]+>)/gm;
@@ -191,9 +207,9 @@ async function findCode(document: SkinnyTextDocument, engine: MarkdownEngine): P
return { multiline, inline };
}
-function isLinkInsideCode(code: CodeInDocument, link: MdLink) {
- return code.multiline.some(interval => link.sourceHrefRange.start.line >= interval[0] && link.sourceHrefRange.start.line < interval[1]) ||
- code.inline.some(position => position.intersection(link.sourceHrefRange));
+function isLinkInsideCode(code: CodeInDocument, linkHrefRange: vscode.Range) {
+ return code.multiline.some(interval => linkHrefRange.start.line >= interval[0] && linkHrefRange.start.line < interval[1]) ||
+ code.inline.some(position => position.intersection(linkHrefRange));
}
export class MdLinkProvider implements vscode.DocumentLinkProvider {
@@ -206,7 +222,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
document: SkinnyTextDocument,
token: vscode.CancellationToken
): Promise<vscode.DocumentLink[]> {
- const allLinks = await this.getAllLinks(document);
+ const allLinks = await this.getAllLinks(document, token);
if (token.isCancellationRequested) {
return [];
}
@@ -219,11 +235,11 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
private toValidDocumentLink(link: MdLink, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined {
switch (link.href.kind) {
case 'external': {
- return new vscode.DocumentLink(link.sourceHrefRange, link.href.uri);
+ return new vscode.DocumentLink(link.source.hrefRange, link.href.uri);
}
case 'internal': {
- const uri = OpenDocumentLinkCommand.createCommandUri(link.sourceResource, link.href.path, link.href.fragment);
- const documentLink = new vscode.DocumentLink(link.sourceHrefRange, uri);
+ const uri = OpenDocumentLinkCommand.createCommandUri(link.source.resource, link.href.path, link.href.fragment);
+ const documentLink = new vscode.DocumentLink(link.source.hrefRange, uri);
documentLink.tooltip = localize('documentLink.tooltip', 'Follow link');
return documentLink;
}
@@ -231,8 +247,8 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
const def = definitionSet.lookup(link.href.ref);
if (def) {
return new vscode.DocumentLink(
- link.sourceHrefRange,
- vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([def.sourceHrefRange.start.line, def.sourceHrefRange.start.character]))}`));
+ link.source.hrefRange,
+ vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([def.source.hrefRange.start.line, def.source.hrefRange.start.character]))}`));
} else {
return undefined;
}
@@ -240,33 +256,64 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
}
}
- public async getAllLinks(document: SkinnyTextDocument): Promise<MdLink[]> {
+ public async getAllLinks(document: SkinnyTextDocument, token: vscode.CancellationToken): Promise<MdLink[]> {
+ const codeInDocument = await findCode(document, this.engine);
+ if (token.isCancellationRequested) {
+ return [];
+ }
+
return Array.from([
- ...(await this.getInlineLinks(document)),
- ...this.getReferenceLinks(document),
- ...this.getLinkDefinitions(document),
+ ...this.getInlineLinks(document, codeInDocument),
+ ...this.getReferenceLinks(document, codeInDocument),
+ ...this.getLinkDefinitions2(document, codeInDocument),
+ ...this.getAutoLinks(document, codeInDocument),
]);
}
- private async getInlineLinks(document: SkinnyTextDocument): Promise<MdLink[]> {
+ private *getInlineLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable<MdLink> {
const text = document.getText();
- const results: MdLink[] = [];
- const codeInDocument = await findCode(document, this.engine);
for (const match of text.matchAll(linkPattern)) {
const matchImageData = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index);
- if (matchImageData && !isLinkInsideCode(codeInDocument, matchImageData)) {
- results.push(matchImageData);
+ if (matchImageData && !isLinkInsideCode(codeInDocument, matchImageData.source.hrefRange)) {
+ yield matchImageData;
}
const matchLinkData = extractDocumentLink(document, match[1].length, match[5], match.index);
- if (matchLinkData && !isLinkInsideCode(codeInDocument, matchLinkData)) {
- results.push(matchLinkData);
+ if (matchLinkData && !isLinkInsideCode(codeInDocument, matchLinkData.source.hrefRange)) {
+ yield matchLinkData;
+ }
+ }
+ }
+
+ private *getAutoLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable<MdLink> {
+ const text = document.getText();
+
+ for (const match of text.matchAll(autoLinkPattern)) {
+ const link = match[1];
+ const linkTarget = parseLink(document, link);
+ if (linkTarget) {
+ const offset = (match.index ?? 0) + 1;
+ const linkStart = document.positionAt(offset);
+ const linkEnd = document.positionAt(offset + link.length);
+ const hrefRange = new vscode.Range(linkStart, linkEnd);
+ if (isLinkInsideCode(codeInDocument, hrefRange)) {
+ continue;
+ }
+ yield {
+ kind: 'link',
+ href: linkTarget,
+ source: {
+ text: link,
+ resource: document.uri,
+ hrefRange: new vscode.Range(linkStart, linkEnd),
+ fragmentRange: getFragmentRange(link, linkStart, linkEnd),
+ }
+ };
}
}
- return results;
}
- private *getReferenceLinks(document: SkinnyTextDocument): Iterable<MdLink> {
+ private *getReferenceLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable<MdLink> {
const text = document.getText();
for (const match of text.matchAll(referenceLinkPattern)) {
let linkStart: vscode.Position;
@@ -286,11 +333,19 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
continue;
}
+ const hrefRange = new vscode.Range(linkStart, linkEnd);
+ if (isLinkInsideCode(codeInDocument, hrefRange)) {
+ continue;
+ }
+
yield {
kind: 'link',
- sourceText: reference,
- sourceHrefRange: new vscode.Range(linkStart, linkEnd),
- sourceResource: document.uri,
+ source: {
+ text: reference,
+ resource: document.uri,
+ hrefRange,
+ fragmentRange: undefined,
+ },
href: {
kind: 'reference',
ref: reference,
@@ -299,7 +354,12 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
}
}
- public *getLinkDefinitions(document: SkinnyTextDocument): Iterable<MdLinkDefinition> {
+ public async getLinkDefinitions(document: SkinnyTextDocument): Promise<Iterable<MdLinkDefinition>> {
+ const codeInDocument = await findCode(document, this.engine);
+ return this.getLinkDefinitions2(document, codeInDocument);
+ }
+
+ private *getLinkDefinitions2(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable<MdLinkDefinition> {
const text = document.getText();
for (const match of text.matchAll(definitionPattern)) {
const pre = match[1];
@@ -310,37 +370,35 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
const refStart = document.positionAt((match.index ?? 0) + 1);
const refRange = new vscode.Range(refStart, refStart.translate({ characterDelta: reference.length }));
+ let linkStart: vscode.Position;
+ let linkEnd: vscode.Position;
+ let text: string;
if (angleBracketLinkRe.test(link)) {
- const linkStart = document.positionAt(offset + 1);
- const linkEnd = document.positionAt(offset + link.length - 1);
- const text = link.substring(1, link.length - 1);
- const target = parseLink(document, text);
- if (target) {
- yield {
- kind: 'definition',
- sourceText: link,
- sourceResource: document.uri,
- sourceHrefRange: new vscode.Range(linkStart, linkEnd),
- refRange,
- ref: reference,
- href: target,
- };
- }
+ linkStart = document.positionAt(offset + 1);
+ linkEnd = document.positionAt(offset + link.length - 1);
+ text = link.substring(1, link.length - 1);
} else {
- const linkStart = document.positionAt(offset);
- const linkEnd = document.positionAt(offset + link.length);
- const target = parseLink(document, link);
- if (target) {
- yield {
- kind: 'definition',
- sourceText: link,
- sourceResource: document.uri,
- sourceHrefRange: new vscode.Range(linkStart, linkEnd),
- refRange,
- ref: reference,
- href: target,
- };
- }
+ linkStart = document.positionAt(offset);
+ linkEnd = document.positionAt(offset + link.length);
+ text = link;
+ }
+ const hrefRange = new vscode.Range(linkStart, linkEnd);
+ if (isLinkInsideCode(codeInDocument, hrefRange)) {
+ continue;
+ }
+ const target = parseLink(document, text);
+ if (target) {
+ yield {
+ kind: 'definition',
+ source: {
+ text: link,
+ resource: document.uri,
+ hrefRange,
+ fragmentRange: getFragmentRange(link, linkStart, linkEnd),
+ },
+ ref: { text: reference, range: refRange },
+ href: target,
+ };
}
}
}
@@ -352,7 +410,7 @@ export class LinkDefinitionSet {
constructor(links: Iterable<MdLink>) {
for (const link of links) {
if (link.kind === 'definition') {
- this._map.set(link.ref, link);
+ this._map.set(link.ref.text, link);
}
}
}