mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
Trying to clean up md link structure by moving defintion up a level
This commit is contained in:
parent
0e65adbda8
commit
57dffde3d4
|
@ -30,13 +30,7 @@ export interface ReferenceLinkTarget {
|
|||
readonly ref: string;
|
||||
}
|
||||
|
||||
export interface DefinitionLinkTarget {
|
||||
readonly kind: 'definition';
|
||||
readonly ref: string;
|
||||
readonly target: ExternalLinkTarget | InternalLinkTarget;
|
||||
}
|
||||
|
||||
export type LinkTarget = ExternalLinkTarget | InternalLinkTarget | ReferenceLinkTarget | DefinitionLinkTarget;
|
||||
export type LinkTarget = ExternalLinkTarget | InternalLinkTarget | ReferenceLinkTarget;
|
||||
|
||||
|
||||
function parseLink(
|
||||
|
@ -93,7 +87,9 @@ function getWorkspaceFolder(document: SkinnyTextDocument) {
|
|||
|| vscode.workspace.workspaceFolders?.[0]?.uri;
|
||||
}
|
||||
|
||||
export interface LinkData {
|
||||
interface MdInlineLink {
|
||||
readonly kind: 'link';
|
||||
|
||||
readonly target: LinkTarget;
|
||||
|
||||
readonly sourceText: string;
|
||||
|
@ -101,12 +97,25 @@ export interface LinkData {
|
|||
readonly sourceRange: vscode.Range;
|
||||
}
|
||||
|
||||
export interface MdLinkDefinition {
|
||||
readonly kind: 'definition';
|
||||
|
||||
readonly sourceText: string;
|
||||
readonly sourceResource: vscode.Uri;
|
||||
readonly sourceRange: vscode.Range;
|
||||
|
||||
readonly ref: string;
|
||||
readonly target: ExternalLinkTarget | InternalLinkTarget;
|
||||
}
|
||||
|
||||
export type MdLink = MdInlineLink | MdLinkDefinition;
|
||||
|
||||
function extractDocumentLink(
|
||||
document: SkinnyTextDocument,
|
||||
pre: number,
|
||||
link: string,
|
||||
matchIndex: number | undefined
|
||||
): LinkData | undefined {
|
||||
): MdLink | undefined {
|
||||
const offset = (matchIndex || 0) + pre;
|
||||
const linkStart = document.positionAt(offset);
|
||||
const linkEnd = document.positionAt(offset + link.length);
|
||||
|
@ -116,6 +125,7 @@ function extractDocumentLink(
|
|||
return undefined;
|
||||
}
|
||||
return {
|
||||
kind: 'link',
|
||||
target: linkTarget,
|
||||
sourceText: link,
|
||||
sourceResource: document.uri,
|
||||
|
@ -179,7 +189,7 @@ async function findCode(document: SkinnyTextDocument, engine: MarkdownEngine): P
|
|||
return { multiline, inline };
|
||||
}
|
||||
|
||||
function isLinkInsideCode(code: CodeInDocument, link: LinkData) {
|
||||
function isLinkInsideCode(code: CodeInDocument, link: MdLink) {
|
||||
return code.multiline.some(interval => link.sourceRange.start.line >= interval[0] && link.sourceRange.start.line < interval[1]) ||
|
||||
code.inline.some(position => position.intersection(link.sourceRange));
|
||||
}
|
||||
|
@ -204,7 +214,17 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
.map(data => this.toValidDocumentLink(data, definitionSet)));
|
||||
}
|
||||
|
||||
private toValidDocumentLink(link: LinkData, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined {
|
||||
private toValidDocumentLink(link: MdLink, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined {
|
||||
if (link.kind === 'definition') {
|
||||
return this.toValidDocumentLink({
|
||||
kind: 'link',
|
||||
sourceText: link.sourceText,
|
||||
sourceRange: link.sourceRange,
|
||||
sourceResource: link.sourceResource,
|
||||
target: link.target
|
||||
}, definitionSet);
|
||||
}
|
||||
|
||||
switch (link.target.kind) {
|
||||
case 'external': {
|
||||
return new vscode.DocumentLink(link.sourceRange, link.target.uri);
|
||||
|
@ -225,28 +245,21 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
return undefined;
|
||||
}
|
||||
}
|
||||
case 'definition':
|
||||
return this.toValidDocumentLink({
|
||||
sourceText: link.sourceText,
|
||||
sourceRange: link.sourceRange,
|
||||
sourceResource: link.sourceResource,
|
||||
target: link.target.target
|
||||
}, definitionSet);
|
||||
}
|
||||
}
|
||||
|
||||
public async getAllLinks(document: SkinnyTextDocument): Promise<LinkData[]> {
|
||||
public async getAllLinks(document: SkinnyTextDocument): Promise<MdLink[]> {
|
||||
return Array.from([
|
||||
...(await this.getInlineLinks(document)),
|
||||
...this.getReferenceLinks(document),
|
||||
...this.getDefinitionLinks(document),
|
||||
...this.getLinkDefinitions(document),
|
||||
]);
|
||||
}
|
||||
|
||||
private async getInlineLinks(document: SkinnyTextDocument): Promise<LinkData[]> {
|
||||
private async getInlineLinks(document: SkinnyTextDocument): Promise<MdLink[]> {
|
||||
const text = document.getText();
|
||||
|
||||
const results: LinkData[] = [];
|
||||
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);
|
||||
|
@ -261,7 +274,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
return results;
|
||||
}
|
||||
|
||||
private *getReferenceLinks(document: SkinnyTextDocument): Iterable<LinkData> {
|
||||
private *getReferenceLinks(document: SkinnyTextDocument): Iterable<MdLink> {
|
||||
const text = document.getText();
|
||||
for (const match of text.matchAll(referenceLinkPattern)) {
|
||||
let linkStart: vscode.Position;
|
||||
|
@ -282,6 +295,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
}
|
||||
|
||||
yield {
|
||||
kind: 'link',
|
||||
sourceText: reference,
|
||||
sourceRange: new vscode.Range(linkStart, linkEnd),
|
||||
sourceResource: document.uri,
|
||||
|
@ -293,7 +307,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
}
|
||||
}
|
||||
|
||||
public *getDefinitionLinks(document: SkinnyTextDocument): Iterable<LinkData> {
|
||||
public *getLinkDefinitions(document: SkinnyTextDocument): Iterable<MdLinkDefinition> {
|
||||
const text = document.getText();
|
||||
for (const match of text.matchAll(definitionPattern)) {
|
||||
const pre = match[1];
|
||||
|
@ -308,14 +322,13 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
const target = parseLink(document, text);
|
||||
if (target) {
|
||||
yield {
|
||||
kind: 'definition',
|
||||
sourceText: link,
|
||||
sourceResource: document.uri,
|
||||
sourceRange: new vscode.Range(linkStart, linkEnd),
|
||||
target: {
|
||||
kind: 'definition',
|
||||
|
||||
ref: reference,
|
||||
target
|
||||
}
|
||||
target,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
|
@ -324,14 +337,12 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
const target = parseLink(document, link);
|
||||
if (target) {
|
||||
yield {
|
||||
kind: 'definition',
|
||||
sourceText: link,
|
||||
sourceResource: document.uri,
|
||||
sourceRange: new vscode.Range(linkStart, linkEnd),
|
||||
target: {
|
||||
kind: 'definition',
|
||||
ref: reference,
|
||||
target,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -340,17 +351,17 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
|||
}
|
||||
|
||||
export class LinkDefinitionSet {
|
||||
private readonly _map = new Map<string, LinkData>();
|
||||
private readonly _map = new Map<string, MdLink>();
|
||||
|
||||
constructor(links: Iterable<LinkData>) {
|
||||
constructor(links: Iterable<MdLink>) {
|
||||
for (const link of links) {
|
||||
if (link.target.kind === 'definition') {
|
||||
this._map.set(link.target.ref, link);
|
||||
if (link.kind === 'definition') {
|
||||
this._map.set(link.ref, link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public lookup(ref: string): LinkData | undefined {
|
||||
public lookup(ref: string): MdLink | 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 { DefinitionLinkTarget, MdLinkProvider } from './documentLinkProvider';
|
||||
import { 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.getDefinitionLinks(document);
|
||||
const definitions = this.linkProvider.getLinkDefinitions(document);
|
||||
for (const def of definitions) {
|
||||
yield {
|
||||
kind: vscode.CompletionItemKind.Reference,
|
||||
label: (def.target as DefinitionLinkTarget).ref,
|
||||
label: def.ref,
|
||||
range: {
|
||||
inserting: insertionRange,
|
||||
replacing: replacementRange,
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Slugifier } from '../slugify';
|
|||
import { TableOfContents, TocEntry } from '../tableOfContents';
|
||||
import { Disposable } from '../util/dispose';
|
||||
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
|
||||
import { DefinitionLinkTarget, InternalLinkTarget, LinkData, LinkTarget, MdLinkProvider } from './documentLinkProvider';
|
||||
import { InternalLinkTarget, MdLink, LinkTarget, MdLinkProvider, MdLinkDefinition } from './documentLinkProvider';
|
||||
import { MdWorkspaceCache } from './workspaceCache';
|
||||
|
||||
|
||||
|
@ -59,7 +59,7 @@ interface MdHeaderReference {
|
|||
export type MdReference = MdLinkReference | MdHeaderReference;
|
||||
|
||||
|
||||
function getFragmentLocation(link: LinkData): vscode.Location | undefined {
|
||||
function getFragmentLocation(link: MdLink): vscode.Location | undefined {
|
||||
const index = link.sourceText.indexOf('#');
|
||||
if (index < 0) {
|
||||
return undefined;
|
||||
|
@ -71,7 +71,7 @@ function getFragmentLocation(link: LinkData): vscode.Location | undefined {
|
|||
|
||||
export class MdReferencesProvider extends Disposable implements vscode.ReferenceProvider {
|
||||
|
||||
private readonly _linkCache: MdWorkspaceCache<LinkData[]>;
|
||||
private readonly _linkCache: MdWorkspaceCache<MdLink[]>;
|
||||
|
||||
public constructor(
|
||||
private readonly linkProvider: MdLinkProvider,
|
||||
|
@ -129,7 +129,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|
|||
location: new vscode.Location(link.sourceResource, link.sourceRange),
|
||||
fragmentLocation: getFragmentLocation(link),
|
||||
});
|
||||
} else if (link.target.kind === 'definition' && isLinkToHeader(link.target.target, header, document.uri, this.slugifier)) {
|
||||
} else if (link.kind === 'definition' && isLinkToHeader(link.target, header, document.uri, this.slugifier)) {
|
||||
references.push({
|
||||
kind: 'link',
|
||||
isTriggerLocation: false,
|
||||
|
@ -149,9 +149,9 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|
|||
return sourceLink ? this.getReferencesToLink(sourceLink) : [];
|
||||
}
|
||||
|
||||
private async getReferencesToLink(sourceLink: LinkData): Promise<MdReference[]> {
|
||||
if (sourceLink.target.kind === 'definition') {
|
||||
return this.getReferencesToLink(this.getInnerLink(sourceLink, sourceLink.target));
|
||||
private async getReferencesToLink(sourceLink: MdLink): Promise<MdReference[]> {
|
||||
if (sourceLink.kind === 'definition') {
|
||||
return this.getReferencesToLink(this.getInnerLink(sourceLink));
|
||||
}
|
||||
|
||||
const allLinksInWorkspace = (await this._linkCache.getAll()).flat();
|
||||
|
@ -194,8 +194,8 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|
|||
}
|
||||
|
||||
for (let link of allLinksInWorkspace) {
|
||||
if (link.target.kind === 'definition') {
|
||||
link = this.getInnerLink(link, link.target);
|
||||
if (link.kind === 'definition') {
|
||||
link = this.getInnerLink(link);
|
||||
}
|
||||
|
||||
if (link.target.kind !== 'internal') {
|
||||
|
@ -239,28 +239,40 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|
|||
return references;
|
||||
}
|
||||
|
||||
private getInnerLink(sourceLink: LinkData, target: DefinitionLinkTarget): LinkData {
|
||||
private getInnerLink(sourceLink: MdLinkDefinition): MdLink {
|
||||
return {
|
||||
kind: 'link',
|
||||
sourceText: sourceLink.sourceText, // This is not correct
|
||||
sourceResource: sourceLink.sourceResource,
|
||||
sourceRange: sourceLink.sourceRange,
|
||||
target: target.target,
|
||||
target: sourceLink.target,
|
||||
};
|
||||
}
|
||||
|
||||
private * getReferencesToReferenceLink(allLinks: Iterable<LinkData>, sourceLink: LinkData): Iterable<MdReference> {
|
||||
private * getReferencesToReferenceLink(allLinks: Iterable<MdLink>, sourceLink: MdLink): Iterable<MdReference> {
|
||||
if (sourceLink.target.kind !== 'reference') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const link of allLinks) {
|
||||
if (link.target.kind === 'reference' || link.target.kind === 'definition') {
|
||||
if (link.kind === 'definition') {
|
||||
if (link.ref === sourceLink.target.ref && link.sourceResource.fsPath === sourceLink.sourceResource.fsPath) {
|
||||
const isTriggerLocation = sourceLink.sourceResource.fsPath === link.sourceResource.fsPath && sourceLink.sourceRange.isEqual(link.sourceRange);
|
||||
yield {
|
||||
kind: 'link',
|
||||
isTriggerLocation,
|
||||
isDefinition: true,
|
||||
location: new vscode.Location(sourceLink.sourceResource, link.sourceRange),
|
||||
fragmentLocation: getFragmentLocation(link),
|
||||
};
|
||||
}
|
||||
} else if (link.target.kind === 'reference') {
|
||||
if (link.target.ref === sourceLink.target.ref && link.sourceResource.fsPath === sourceLink.sourceResource.fsPath) {
|
||||
const isTriggerLocation = sourceLink.sourceResource.fsPath === link.sourceResource.fsPath && sourceLink.sourceRange.isEqual(link.sourceRange);
|
||||
yield {
|
||||
kind: 'link',
|
||||
isTriggerLocation,
|
||||
isDefinition: link.target.kind === 'definition',
|
||||
isDefinition: false,
|
||||
location: new vscode.Location(sourceLink.sourceResource, link.sourceRange),
|
||||
fragmentLocation: getFragmentLocation(link),
|
||||
};
|
||||
|
|
|
@ -59,7 +59,7 @@ function assertEditsEqual(actualEdit: vscode.WorkspaceEdit, ...expectedEdits: {
|
|||
}
|
||||
}
|
||||
|
||||
suite.only('markdown: rename', () => {
|
||||
suite('markdown: rename', () => {
|
||||
|
||||
setup(async () => {
|
||||
// the tests make the assumption that link providers are already registered
|
||||
|
|
Loading…
Reference in a new issue