mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
Include invalid references as targets for find all references
For #146277
This commit is contained in:
parent
17783a09e5
commit
d441406b1e
|
@ -8,6 +8,7 @@ import * as nls from 'vscode-nls';
|
|||
import * as uri from 'vscode-uri';
|
||||
import { OpenDocumentLinkCommand } from '../commands/openDocumentLink';
|
||||
import { MarkdownEngine } from '../markdownEngine';
|
||||
import { coalesce } from '../util/arrays';
|
||||
import { getUriForLinkWithKnownExternalScheme, isOfScheme, Schemes } from '../util/schemes';
|
||||
import { SkinnyTextDocument } from '../workspaceContents';
|
||||
|
||||
|
@ -34,7 +35,6 @@ export interface ReferenceLinkTarget {
|
|||
readonly fromResource: vscode.Uri;
|
||||
|
||||
readonly ref: string;
|
||||
readonly position: vscode.Position;
|
||||
}
|
||||
|
||||
export interface DefinitionLinkTarget {
|
||||
|
@ -192,7 +192,7 @@ function isLinkInsideCode(code: CodeInDocument, link: LinkData) {
|
|||
code.inline.some(position => position.intersection(link.sourceRange));
|
||||
}
|
||||
|
||||
function createDocumentLink(sourceRange: vscode.Range, target: LinkTarget): vscode.DocumentLink {
|
||||
function createDocumentLink(sourceRange: vscode.Range, target: LinkTarget, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined {
|
||||
switch (target.kind) {
|
||||
case 'external': {
|
||||
return new vscode.DocumentLink(sourceRange, target.uri);
|
||||
|
@ -204,25 +204,38 @@ function createDocumentLink(sourceRange: vscode.Range, target: LinkTarget): vsco
|
|||
return documentLink;
|
||||
}
|
||||
case 'reference': {
|
||||
return new vscode.DocumentLink(
|
||||
sourceRange,
|
||||
vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([target.position.line, target.position.character]))}`));
|
||||
const def = definitionSet.lookup(target.ref);
|
||||
if (def) {
|
||||
return new vscode.DocumentLink(
|
||||
sourceRange,
|
||||
vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([def.sourceRange.start.line, def.sourceRange.start.character]))}`));
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
case 'definition':
|
||||
return createDocumentLink(sourceRange, target.target);
|
||||
return createDocumentLink(sourceRange, target.target, definitionSet);
|
||||
}
|
||||
}
|
||||
|
||||
export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
|
||||
constructor(
|
||||
private readonly engine: MarkdownEngine
|
||||
) { }
|
||||
|
||||
public async provideDocumentLinks(
|
||||
document: SkinnyTextDocument,
|
||||
_token: vscode.CancellationToken
|
||||
token: vscode.CancellationToken
|
||||
): Promise<vscode.DocumentLink[]> {
|
||||
return (await this.getAllLinks(document)).map(data => createDocumentLink(data.sourceRange, data.target));
|
||||
const allLinks = await this.getAllLinks(document);
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const definitionSet = new LinkDefinitionSet(allLinks);
|
||||
return coalesce(allLinks
|
||||
.map(data => createDocumentLink(data.sourceRange, data.target, definitionSet)));
|
||||
}
|
||||
|
||||
public async getAllLinks(document: SkinnyTextDocument): Promise<LinkData[]> {
|
||||
|
@ -253,7 +266,6 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
|
||||
public *getReferenceLinks(document: SkinnyTextDocument): Iterable<LinkData> {
|
||||
const text = document.getText();
|
||||
const definitions = this.getDefinitions(document);
|
||||
for (const match of text.matchAll(referenceLinkPattern)) {
|
||||
let linkStart: vscode.Position;
|
||||
let linkEnd: vscode.Position;
|
||||
|
@ -272,46 +284,19 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
continue;
|
||||
}
|
||||
|
||||
const link = definitions.get(reference);
|
||||
if (link) {
|
||||
yield {
|
||||
sourceRange: new vscode.Range(linkStart, linkEnd),
|
||||
target: {
|
||||
kind: 'reference',
|
||||
fromResource: document.uri,
|
||||
ref: reference,
|
||||
position: link.linkRange.start
|
||||
}
|
||||
};
|
||||
}
|
||||
yield {
|
||||
sourceRange: new vscode.Range(linkStart, linkEnd),
|
||||
target: {
|
||||
kind: 'reference',
|
||||
fromResource: document.uri,
|
||||
ref: reference,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public *getDefinitionLinks(document: SkinnyTextDocument): Iterable<LinkData> {
|
||||
const definitions = this.getDefinitions(document);
|
||||
for (const [ref, definition] of definitions) {
|
||||
try {
|
||||
const target = parseLink(document, definition.link);
|
||||
if (target) {
|
||||
yield {
|
||||
sourceRange: definition.linkRange,
|
||||
target: {
|
||||
fromResource: document.uri,
|
||||
ref,
|
||||
kind: 'definition',
|
||||
target
|
||||
}
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getDefinitions(document: SkinnyTextDocument): Map<string, { readonly link: string; readonly linkRange: vscode.Range }> {
|
||||
const text = document.getText();
|
||||
const out = new Map<string, { link: string; linkRange: vscode.Range }>();
|
||||
for (const match of text.matchAll(definitionPattern)) {
|
||||
const pre = match[1];
|
||||
const reference = match[2];
|
||||
|
@ -321,19 +306,50 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
if (angleBracketLinkRe.test(link)) {
|
||||
const linkStart = document.positionAt(offset + 1);
|
||||
const linkEnd = document.positionAt(offset + link.length - 1);
|
||||
out.set(reference, {
|
||||
link: link.substring(1, link.length - 1),
|
||||
linkRange: new vscode.Range(linkStart, linkEnd)
|
||||
});
|
||||
const target = parseLink(document, link.substring(1, link.length - 1));
|
||||
if (target) {
|
||||
yield {
|
||||
sourceRange: new vscode.Range(linkStart, linkEnd),
|
||||
target: {
|
||||
kind: 'definition',
|
||||
fromResource: document.uri,
|
||||
ref: reference,
|
||||
target
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
const linkStart = document.positionAt(offset);
|
||||
const linkEnd = document.positionAt(offset + link.length);
|
||||
out.set(reference, {
|
||||
link: link,
|
||||
linkRange: new vscode.Range(linkStart, linkEnd)
|
||||
});
|
||||
const target = parseLink(document, link);
|
||||
if (target) {
|
||||
yield {
|
||||
sourceRange: new vscode.Range(linkStart, linkEnd),
|
||||
target: {
|
||||
kind: 'definition',
|
||||
fromResource: document.uri,
|
||||
ref: reference,
|
||||
target,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
export class LinkDefinitionSet {
|
||||
private readonly _map = new Map<string, LinkData>();
|
||||
|
||||
constructor(links: Iterable<LinkData>) {
|
||||
for (const link of links) {
|
||||
if (link.target.kind === 'definition') {
|
||||
this._map.set(link.target.ref, link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public lookup(ref: string): LinkData | undefined {
|
||||
return this._map.get(ref);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { MarkdownEngine } from '../markdownEngine';
|
|||
import { TableOfContents } from '../tableOfContents';
|
||||
import { resolveUriToMarkdownFile } from '../util/openDocumentLink';
|
||||
import { SkinnyTextDocument } from '../workspaceContents';
|
||||
import { MdLinkProvider } from './documentLinkProvider';
|
||||
import { DefinitionLinkTarget, MdLinkProvider } from './documentLinkProvider';
|
||||
|
||||
enum CompletionContextKind {
|
||||
/** `[...](|)` */
|
||||
|
@ -236,11 +236,11 @@ export class MdPathCompletionProvider implements vscode.CompletionItemProvider {
|
|||
const insertionRange = new vscode.Range(context.linkTextStartPosition, position);
|
||||
const replacementRange = new vscode.Range(insertionRange.start, position.translate({ characterDelta: context.linkSuffix.length }));
|
||||
|
||||
const definitions = this.linkProvider.getDefinitions(document);
|
||||
const definitions = this.linkProvider.getDefinitionLinks(document);
|
||||
for (const def of definitions) {
|
||||
yield {
|
||||
kind: vscode.CompletionItemKind.Reference,
|
||||
label: def[0],
|
||||
label: (def.target as DefinitionLinkTarget).ref,
|
||||
range: {
|
||||
inserting: insertionRange,
|
||||
replacing: replacementRange,
|
||||
|
|
|
@ -15,4 +15,11 @@ export function equals<T>(one: ReadonlyArray<T>, other: ReadonlyArray<T>, itemEq
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns New array with all falsy values removed. The original array IS NOT modified.
|
||||
*/
|
||||
export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
|
||||
return <T[]>array.filter(e => !!e);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue