mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-19 16:18:58 +01:00
Split markdown linkComputer from linkProvider (#152124)
Instead of passing around a full `vscode.DocumentLinkProvider`, we should pass just the more minimal interface that consumers need
This commit is contained in:
@@ -3,20 +3,20 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as picomatch from 'picomatch';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as picomatch from 'picomatch';
|
||||
import { CommandManager } from '../commandManager';
|
||||
import { MarkdownEngine } from '../markdownEngine';
|
||||
import { TableOfContents } from '../tableOfContents';
|
||||
import { Delayer } from '../util/async';
|
||||
import { Disposable } from '../util/dispose';
|
||||
import { isMarkdownFile } from '../util/file';
|
||||
import { Limiter } from '../util/limiter';
|
||||
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
|
||||
import { InternalHref, LinkDefinitionSet, MdLink, MdLinkProvider, MdLinkSource } from './documentLinkProvider';
|
||||
import { tryFindMdDocumentForLink } from './references';
|
||||
import { CommandManager } from '../commandManager';
|
||||
import { ResourceMap } from '../util/resourceMap';
|
||||
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
|
||||
import { InternalHref, LinkDefinitionSet, MdLink, MdLinkComputer, MdLinkSource } from './documentLinkProvider';
|
||||
import { tryFindMdDocumentForLink } from './references';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -382,11 +382,11 @@ export class DiagnosticComputer {
|
||||
constructor(
|
||||
private readonly engine: MarkdownEngine,
|
||||
private readonly workspaceContents: MdWorkspaceContents,
|
||||
private readonly linkProvider: MdLinkProvider,
|
||||
private readonly linkComputer: MdLinkComputer,
|
||||
) { }
|
||||
|
||||
public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise<{ readonly diagnostics: vscode.Diagnostic[]; readonly links: MdLink[] }> {
|
||||
const links = await this.linkProvider.getAllLinks(doc, token);
|
||||
const links = await this.linkComputer.getAllLinks(doc, token);
|
||||
if (token.isCancellationRequested) {
|
||||
return { links, diagnostics: [] };
|
||||
}
|
||||
@@ -559,11 +559,11 @@ export function register(
|
||||
selector: vscode.DocumentSelector,
|
||||
engine: MarkdownEngine,
|
||||
workspaceContents: MdWorkspaceContents,
|
||||
linkProvider: MdLinkProvider,
|
||||
linkComputer: MdLinkComputer,
|
||||
commandManager: CommandManager,
|
||||
): vscode.Disposable {
|
||||
const configuration = new VSCodeDiagnosticConfiguration();
|
||||
const manager = new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkProvider), configuration);
|
||||
const manager = new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkComputer), configuration);
|
||||
return vscode.Disposable.from(
|
||||
configuration,
|
||||
manager,
|
||||
|
||||
@@ -242,53 +242,12 @@ class NoLinkRanges {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
export class MdLinkComputer {
|
||||
|
||||
constructor(
|
||||
private readonly engine: MarkdownEngine
|
||||
) { }
|
||||
|
||||
public async provideDocumentLinks(
|
||||
document: SkinnyTextDocument,
|
||||
token: vscode.CancellationToken
|
||||
): Promise<vscode.DocumentLink[]> {
|
||||
const allLinks = await this.getAllLinks(document, token);
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const definitionSet = new LinkDefinitionSet(allLinks);
|
||||
return coalesce(allLinks
|
||||
.map(data => this.toValidDocumentLink(data, definitionSet)));
|
||||
}
|
||||
|
||||
private toValidDocumentLink(link: MdLink, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined {
|
||||
switch (link.href.kind) {
|
||||
case 'external': {
|
||||
return new vscode.DocumentLink(link.source.hrefRange, link.href.uri);
|
||||
}
|
||||
case 'internal': {
|
||||
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;
|
||||
}
|
||||
case 'reference': {
|
||||
const def = definitionSet.lookup(link.href.ref);
|
||||
if (def) {
|
||||
const documentLink = new vscode.DocumentLink(
|
||||
link.source.hrefRange,
|
||||
vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([def.source.hrefRange.start.line, def.source.hrefRange.start.character]))}`));
|
||||
documentLink.tooltip = localize('documentLink.referenceTooltip', 'Go to link definition');
|
||||
return documentLink;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async getAllLinks(document: SkinnyTextDocument, token: vscode.CancellationToken): Promise<MdLink[]> {
|
||||
const noLinkRanges = await NoLinkRanges.compute(document, this.engine);
|
||||
if (token.isCancellationRequested) {
|
||||
@@ -475,3 +434,57 @@ export class LinkDefinitionSet {
|
||||
return this._map.get(ref);
|
||||
}
|
||||
}
|
||||
|
||||
export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
|
||||
constructor(
|
||||
private readonly _linkComputer: MdLinkComputer,
|
||||
) { }
|
||||
|
||||
public async provideDocumentLinks(
|
||||
document: SkinnyTextDocument,
|
||||
token: vscode.CancellationToken
|
||||
): Promise<vscode.DocumentLink[]> {
|
||||
const allLinks = (await this._linkComputer.getAllLinks(document, token)) ?? [];
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const definitionSet = new LinkDefinitionSet(allLinks);
|
||||
return coalesce(allLinks
|
||||
.map(data => this.toValidDocumentLink(data, definitionSet)));
|
||||
}
|
||||
|
||||
private toValidDocumentLink(link: MdLink, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined {
|
||||
switch (link.href.kind) {
|
||||
case 'external': {
|
||||
return new vscode.DocumentLink(link.source.hrefRange, link.href.uri);
|
||||
}
|
||||
case 'internal': {
|
||||
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;
|
||||
}
|
||||
case 'reference': {
|
||||
const def = definitionSet.lookup(link.href.ref);
|
||||
if (def) {
|
||||
const documentLink = new vscode.DocumentLink(
|
||||
link.source.hrefRange,
|
||||
vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([def.source.hrefRange.start.line, def.source.hrefRange.start.character]))}`));
|
||||
documentLink.tooltip = localize('documentLink.referenceTooltip', 'Go to link definition');
|
||||
return documentLink;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function registerDocumentLinkProvider(
|
||||
selector: vscode.DocumentSelector,
|
||||
linkComputer: MdLinkComputer,
|
||||
): vscode.Disposable {
|
||||
return vscode.languages.registerDocumentLinkProvider(selector, new MdLinkProvider(linkComputer));
|
||||
}
|
||||
|
||||
@@ -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 { MdLinkComputer } from './documentLinkProvider';
|
||||
|
||||
enum CompletionContextKind {
|
||||
/** `[...](|)` */
|
||||
@@ -81,14 +81,14 @@ export class MdPathCompletionProvider implements vscode.CompletionItemProvider {
|
||||
public static register(
|
||||
selector: vscode.DocumentSelector,
|
||||
engine: MarkdownEngine,
|
||||
linkProvider: MdLinkProvider,
|
||||
linkComputer: MdLinkComputer,
|
||||
): vscode.Disposable {
|
||||
return vscode.languages.registerCompletionItemProvider(selector, new MdPathCompletionProvider(engine, linkProvider), '.', '/', '#');
|
||||
return vscode.languages.registerCompletionItemProvider(selector, new MdPathCompletionProvider(engine, linkComputer), '.', '/', '#');
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly engine: MarkdownEngine,
|
||||
private readonly linkProvider: MdLinkProvider,
|
||||
private readonly linkComputer: MdLinkComputer,
|
||||
) { }
|
||||
|
||||
public async provideCompletionItems(document: SkinnyTextDocument, position: vscode.Position, _token: vscode.CancellationToken, _context: vscode.CompletionContext): Promise<vscode.CompletionItem[]> {
|
||||
@@ -240,7 +240,7 @@ 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 = await this.linkProvider.getLinkDefinitions(document);
|
||||
const definitions = await this.linkComputer.getLinkDefinitions(document);
|
||||
for (const def of definitions) {
|
||||
yield {
|
||||
kind: vscode.CompletionItemKind.Reference,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { TableOfContents, TocEntry } from '../tableOfContents';
|
||||
import { noopToken } from '../util/cancellation';
|
||||
import { Disposable } from '../util/dispose';
|
||||
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
|
||||
import { InternalHref, MdLink, MdLinkProvider } from './documentLinkProvider';
|
||||
import { InternalHref, MdLink, MdLinkComputer } from './documentLinkProvider';
|
||||
import { MdWorkspaceCache } from './workspaceCache';
|
||||
|
||||
|
||||
@@ -64,14 +64,14 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|
||||
private readonly _linkCache: MdWorkspaceCache<readonly MdLink[]>;
|
||||
|
||||
public constructor(
|
||||
private readonly linkProvider: MdLinkProvider,
|
||||
private readonly linkComputer: MdLinkComputer,
|
||||
private readonly workspaceContents: MdWorkspaceContents,
|
||||
private readonly engine: MarkdownEngine,
|
||||
private readonly slugifier: Slugifier,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._linkCache = this._register(new MdWorkspaceCache(workspaceContents, doc => linkProvider.getAllLinks(doc, noopToken)));
|
||||
this._linkCache = this._register(new MdWorkspaceCache(workspaceContents, doc => linkComputer.getAllLinks(doc, noopToken)));
|
||||
}
|
||||
|
||||
async provideReferences(document: SkinnyTextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise<vscode.Location[] | undefined> {
|
||||
@@ -129,7 +129,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|
||||
}
|
||||
|
||||
private async getReferencesToLinkAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<MdReference[]> {
|
||||
const docLinks = await this.linkProvider.getAllLinks(document, token);
|
||||
const docLinks = await this.linkComputer.getAllLinks(document, token);
|
||||
|
||||
for (const link of docLinks) {
|
||||
if (link.kind === 'definition') {
|
||||
|
||||
Reference in New Issue
Block a user