Skip to content

Commit 57dffde

Browse files
committed
Trying to clean up md link structure by moving defintion up a level
1 parent 0e65adb commit 57dffde

File tree

4 files changed

+80
-57
lines changed

4 files changed

+80
-57
lines changed

extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,7 @@ export interface ReferenceLinkTarget {
3030
readonly ref: string;
3131
}
3232

33-
export interface DefinitionLinkTarget {
34-
readonly kind: 'definition';
35-
readonly ref: string;
36-
readonly target: ExternalLinkTarget | InternalLinkTarget;
37-
}
38-
39-
export type LinkTarget = ExternalLinkTarget | InternalLinkTarget | ReferenceLinkTarget | DefinitionLinkTarget;
33+
export type LinkTarget = ExternalLinkTarget | InternalLinkTarget | ReferenceLinkTarget;
4034

4135

4236
function parseLink(
@@ -93,20 +87,35 @@ function getWorkspaceFolder(document: SkinnyTextDocument) {
9387
|| vscode.workspace.workspaceFolders?.[0]?.uri;
9488
}
9589

96-
export interface LinkData {
90+
interface MdInlineLink {
91+
readonly kind: 'link';
92+
9793
readonly target: LinkTarget;
9894

9995
readonly sourceText: string;
10096
readonly sourceResource: vscode.Uri;
10197
readonly sourceRange: vscode.Range;
10298
}
10399

100+
export interface MdLinkDefinition {
101+
readonly kind: 'definition';
102+
103+
readonly sourceText: string;
104+
readonly sourceResource: vscode.Uri;
105+
readonly sourceRange: vscode.Range;
106+
107+
readonly ref: string;
108+
readonly target: ExternalLinkTarget | InternalLinkTarget;
109+
}
110+
111+
export type MdLink = MdInlineLink | MdLinkDefinition;
112+
104113
function extractDocumentLink(
105114
document: SkinnyTextDocument,
106115
pre: number,
107116
link: string,
108117
matchIndex: number | undefined
109-
): LinkData | undefined {
118+
): MdLink | undefined {
110119
const offset = (matchIndex || 0) + pre;
111120
const linkStart = document.positionAt(offset);
112121
const linkEnd = document.positionAt(offset + link.length);
@@ -116,6 +125,7 @@ function extractDocumentLink(
116125
return undefined;
117126
}
118127
return {
128+
kind: 'link',
119129
target: linkTarget,
120130
sourceText: link,
121131
sourceResource: document.uri,
@@ -179,7 +189,7 @@ async function findCode(document: SkinnyTextDocument, engine: MarkdownEngine): P
179189
return { multiline, inline };
180190
}
181191

182-
function isLinkInsideCode(code: CodeInDocument, link: LinkData) {
192+
function isLinkInsideCode(code: CodeInDocument, link: MdLink) {
183193
return code.multiline.some(interval => link.sourceRange.start.line >= interval[0] && link.sourceRange.start.line < interval[1]) ||
184194
code.inline.some(position => position.intersection(link.sourceRange));
185195
}
@@ -204,7 +214,17 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
204214
.map(data => this.toValidDocumentLink(data, definitionSet)));
205215
}
206216

207-
private toValidDocumentLink(link: LinkData, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined {
217+
private toValidDocumentLink(link: MdLink, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined {
218+
if (link.kind === 'definition') {
219+
return this.toValidDocumentLink({
220+
kind: 'link',
221+
sourceText: link.sourceText,
222+
sourceRange: link.sourceRange,
223+
sourceResource: link.sourceResource,
224+
target: link.target
225+
}, definitionSet);
226+
}
227+
208228
switch (link.target.kind) {
209229
case 'external': {
210230
return new vscode.DocumentLink(link.sourceRange, link.target.uri);
@@ -225,28 +245,21 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
225245
return undefined;
226246
}
227247
}
228-
case 'definition':
229-
return this.toValidDocumentLink({
230-
sourceText: link.sourceText,
231-
sourceRange: link.sourceRange,
232-
sourceResource: link.sourceResource,
233-
target: link.target.target
234-
}, definitionSet);
235248
}
236249
}
237250

238-
public async getAllLinks(document: SkinnyTextDocument): Promise<LinkData[]> {
251+
public async getAllLinks(document: SkinnyTextDocument): Promise<MdLink[]> {
239252
return Array.from([
240253
...(await this.getInlineLinks(document)),
241254
...this.getReferenceLinks(document),
242-
...this.getDefinitionLinks(document),
255+
...this.getLinkDefinitions(document),
243256
]);
244257
}
245258

246-
private async getInlineLinks(document: SkinnyTextDocument): Promise<LinkData[]> {
259+
private async getInlineLinks(document: SkinnyTextDocument): Promise<MdLink[]> {
247260
const text = document.getText();
248261

249-
const results: LinkData[] = [];
262+
const results: MdLink[] = [];
250263
const codeInDocument = await findCode(document, this.engine);
251264
for (const match of text.matchAll(linkPattern)) {
252265
const matchImageData = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index);
@@ -261,7 +274,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
261274
return results;
262275
}
263276

264-
private *getReferenceLinks(document: SkinnyTextDocument): Iterable<LinkData> {
277+
private *getReferenceLinks(document: SkinnyTextDocument): Iterable<MdLink> {
265278
const text = document.getText();
266279
for (const match of text.matchAll(referenceLinkPattern)) {
267280
let linkStart: vscode.Position;
@@ -282,6 +295,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
282295
}
283296

284297
yield {
298+
kind: 'link',
285299
sourceText: reference,
286300
sourceRange: new vscode.Range(linkStart, linkEnd),
287301
sourceResource: document.uri,
@@ -293,7 +307,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
293307
}
294308
}
295309

296-
public *getDefinitionLinks(document: SkinnyTextDocument): Iterable<LinkData> {
310+
public *getLinkDefinitions(document: SkinnyTextDocument): Iterable<MdLinkDefinition> {
297311
const text = document.getText();
298312
for (const match of text.matchAll(definitionPattern)) {
299313
const pre = match[1];
@@ -308,14 +322,13 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
308322
const target = parseLink(document, text);
309323
if (target) {
310324
yield {
325+
kind: 'definition',
311326
sourceText: link,
312327
sourceResource: document.uri,
313328
sourceRange: new vscode.Range(linkStart, linkEnd),
314-
target: {
315-
kind: 'definition',
316-
ref: reference,
317-
target
318-
}
329+
330+
ref: reference,
331+
target,
319332
};
320333
}
321334
} else {
@@ -324,14 +337,12 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
324337
const target = parseLink(document, link);
325338
if (target) {
326339
yield {
340+
kind: 'definition',
327341
sourceText: link,
328342
sourceResource: document.uri,
329343
sourceRange: new vscode.Range(linkStart, linkEnd),
330-
target: {
331-
kind: 'definition',
332-
ref: reference,
333-
target,
334-
}
344+
ref: reference,
345+
target,
335346
};
336347
}
337348
}
@@ -340,17 +351,17 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
340351
}
341352

342353
export class LinkDefinitionSet {
343-
private readonly _map = new Map<string, LinkData>();
354+
private readonly _map = new Map<string, MdLink>();
344355

345-
constructor(links: Iterable<LinkData>) {
356+
constructor(links: Iterable<MdLink>) {
346357
for (const link of links) {
347-
if (link.target.kind === 'definition') {
348-
this._map.set(link.target.ref, link);
358+
if (link.kind === 'definition') {
359+
this._map.set(link.ref, link);
349360
}
350361
}
351362
}
352363

353-
public lookup(ref: string): LinkData | undefined {
364+
public lookup(ref: string): MdLink | undefined {
354365
return this._map.get(ref);
355366
}
356367
}

extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { MarkdownEngine } from '../markdownEngine';
99
import { TableOfContents } from '../tableOfContents';
1010
import { resolveUriToMarkdownFile } from '../util/openDocumentLink';
1111
import { SkinnyTextDocument } from '../workspaceContents';
12-
import { DefinitionLinkTarget, MdLinkProvider } from './documentLinkProvider';
12+
import { MdLinkProvider } from './documentLinkProvider';
1313

1414
enum CompletionContextKind {
1515
/** `[...](|)` */
@@ -236,11 +236,11 @@ export class MdPathCompletionProvider implements vscode.CompletionItemProvider {
236236
const insertionRange = new vscode.Range(context.linkTextStartPosition, position);
237237
const replacementRange = new vscode.Range(insertionRange.start, position.translate({ characterDelta: context.linkSuffix.length }));
238238

239-
const definitions = this.linkProvider.getDefinitionLinks(document);
239+
const definitions = this.linkProvider.getLinkDefinitions(document);
240240
for (const def of definitions) {
241241
yield {
242242
kind: vscode.CompletionItemKind.Reference,
243-
label: (def.target as DefinitionLinkTarget).ref,
243+
label: def.ref,
244244
range: {
245245
inserting: insertionRange,
246246
replacing: replacementRange,

extensions/markdown-language-features/src/languageFeatures/references.ts

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Slugifier } from '../slugify';
99
import { TableOfContents, TocEntry } from '../tableOfContents';
1010
import { Disposable } from '../util/dispose';
1111
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
12-
import { DefinitionLinkTarget, InternalLinkTarget, LinkData, LinkTarget, MdLinkProvider } from './documentLinkProvider';
12+
import { InternalLinkTarget, MdLink, LinkTarget, MdLinkProvider, MdLinkDefinition } from './documentLinkProvider';
1313
import { MdWorkspaceCache } from './workspaceCache';
1414

1515

@@ -59,7 +59,7 @@ interface MdHeaderReference {
5959
export type MdReference = MdLinkReference | MdHeaderReference;
6060

6161

62-
function getFragmentLocation(link: LinkData): vscode.Location | undefined {
62+
function getFragmentLocation(link: MdLink): vscode.Location | undefined {
6363
const index = link.sourceText.indexOf('#');
6464
if (index < 0) {
6565
return undefined;
@@ -71,7 +71,7 @@ function getFragmentLocation(link: LinkData): vscode.Location | undefined {
7171

7272
export class MdReferencesProvider extends Disposable implements vscode.ReferenceProvider {
7373

74-
private readonly _linkCache: MdWorkspaceCache<LinkData[]>;
74+
private readonly _linkCache: MdWorkspaceCache<MdLink[]>;
7575

7676
public constructor(
7777
private readonly linkProvider: MdLinkProvider,
@@ -129,7 +129,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
129129
location: new vscode.Location(link.sourceResource, link.sourceRange),
130130
fragmentLocation: getFragmentLocation(link),
131131
});
132-
} else if (link.target.kind === 'definition' && isLinkToHeader(link.target.target, header, document.uri, this.slugifier)) {
132+
} else if (link.kind === 'definition' && isLinkToHeader(link.target, header, document.uri, this.slugifier)) {
133133
references.push({
134134
kind: 'link',
135135
isTriggerLocation: false,
@@ -149,9 +149,9 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
149149
return sourceLink ? this.getReferencesToLink(sourceLink) : [];
150150
}
151151

152-
private async getReferencesToLink(sourceLink: LinkData): Promise<MdReference[]> {
153-
if (sourceLink.target.kind === 'definition') {
154-
return this.getReferencesToLink(this.getInnerLink(sourceLink, sourceLink.target));
152+
private async getReferencesToLink(sourceLink: MdLink): Promise<MdReference[]> {
153+
if (sourceLink.kind === 'definition') {
154+
return this.getReferencesToLink(this.getInnerLink(sourceLink));
155155
}
156156

157157
const allLinksInWorkspace = (await this._linkCache.getAll()).flat();
@@ -194,8 +194,8 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
194194
}
195195

196196
for (let link of allLinksInWorkspace) {
197-
if (link.target.kind === 'definition') {
198-
link = this.getInnerLink(link, link.target);
197+
if (link.kind === 'definition') {
198+
link = this.getInnerLink(link);
199199
}
200200

201201
if (link.target.kind !== 'internal') {
@@ -239,28 +239,40 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
239239
return references;
240240
}
241241

242-
private getInnerLink(sourceLink: LinkData, target: DefinitionLinkTarget): LinkData {
242+
private getInnerLink(sourceLink: MdLinkDefinition): MdLink {
243243
return {
244+
kind: 'link',
244245
sourceText: sourceLink.sourceText, // This is not correct
245246
sourceResource: sourceLink.sourceResource,
246247
sourceRange: sourceLink.sourceRange,
247-
target: target.target,
248+
target: sourceLink.target,
248249
};
249250
}
250251

251-
private * getReferencesToReferenceLink(allLinks: Iterable<LinkData>, sourceLink: LinkData): Iterable<MdReference> {
252+
private * getReferencesToReferenceLink(allLinks: Iterable<MdLink>, sourceLink: MdLink): Iterable<MdReference> {
252253
if (sourceLink.target.kind !== 'reference') {
253254
return;
254255
}
255256

256257
for (const link of allLinks) {
257-
if (link.target.kind === 'reference' || link.target.kind === 'definition') {
258+
if (link.kind === 'definition') {
259+
if (link.ref === sourceLink.target.ref && link.sourceResource.fsPath === sourceLink.sourceResource.fsPath) {
260+
const isTriggerLocation = sourceLink.sourceResource.fsPath === link.sourceResource.fsPath && sourceLink.sourceRange.isEqual(link.sourceRange);
261+
yield {
262+
kind: 'link',
263+
isTriggerLocation,
264+
isDefinition: true,
265+
location: new vscode.Location(sourceLink.sourceResource, link.sourceRange),
266+
fragmentLocation: getFragmentLocation(link),
267+
};
268+
}
269+
} else if (link.target.kind === 'reference') {
258270
if (link.target.ref === sourceLink.target.ref && link.sourceResource.fsPath === sourceLink.sourceResource.fsPath) {
259271
const isTriggerLocation = sourceLink.sourceResource.fsPath === link.sourceResource.fsPath && sourceLink.sourceRange.isEqual(link.sourceRange);
260272
yield {
261273
kind: 'link',
262274
isTriggerLocation,
263-
isDefinition: link.target.kind === 'definition',
275+
isDefinition: false,
264276
location: new vscode.Location(sourceLink.sourceResource, link.sourceRange),
265277
fragmentLocation: getFragmentLocation(link),
266278
};

extensions/markdown-language-features/src/test/rename.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ function assertEditsEqual(actualEdit: vscode.WorkspaceEdit, ...expectedEdits: {
5959
}
6060
}
6161

62-
suite.only('markdown: rename', () => {
62+
suite('markdown: rename', () => {
6363

6464
setup(async () => {
6565
// the tests make the assumption that link providers are already registered

0 commit comments

Comments
 (0)