Split out VS Code reference provider from markdown reference provider (#152369)

This change renames the main markdown reference provider class to `MdReferenceComputer` and then uses this to implement a `vscode.ReferenceProvider`

This more cleanly splits the VS Code part of the logic from the general reference calculation stuff other providers consume
This commit is contained in:
Matt Bierner 2022-06-16 12:47:48 -07:00 committed by GitHub
parent e8f28ac3ac
commit 3114ee690b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 73 additions and 55 deletions

View file

@ -15,7 +15,7 @@ import { registerDropIntoEditor } from './languageFeatures/dropIntoEditor';
import { registerFindFileReferences } from './languageFeatures/fileReferences';
import { MdFoldingProvider } from './languageFeatures/foldingProvider';
import { MdPathCompletionProvider } from './languageFeatures/pathCompletions';
import { MdReferencesProvider } from './languageFeatures/references';
import { MdReferencesComputer, registerReferencesProvider } from './languageFeatures/references';
import { MdRenameProvider } from './languageFeatures/rename';
import { MdSmartSelect } from './languageFeatures/smartSelect';
import { MdWorkspaceSymbolProvider } from './languageFeatures/workspaceSymbolProvider';
@ -66,22 +66,22 @@ function registerMarkdownLanguageFeatures(
const linkComputer = new MdLinkComputer(engine);
const workspaceContents = new VsCodeMdWorkspaceContents();
const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier);
const referencesComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier);
return vscode.Disposable.from(
workspaceContents,
vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider),
vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine)),
vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine)),
vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider, workspaceContents)),
vscode.languages.registerReferenceProvider(selector, referencesProvider),
vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier)),
vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesProvider)),
vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesComputer, workspaceContents, githubSlugifier)),
vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesComputer)),
MdPathCompletionProvider.register(selector, engine, linkComputer),
registerDocumentLinkProvider(selector, linkComputer),
registerDiagnostics(selector, engine, workspaceContents, linkComputer, commandManager, referencesProvider),
registerDiagnostics(selector, engine, workspaceContents, linkComputer, commandManager, referencesComputer),
registerDropIntoEditor(selector),
registerReferencesProvider(selector, referencesComputer),
registerPasteProvider(selector),
registerFindFileReferences(commandManager, referencesProvider),
registerFindFileReferences(commandManager, referencesComputer),
);
}

View file

@ -5,16 +5,18 @@
import * as vscode from 'vscode';
import { Disposable } from '../util/dispose';
import { SkinnyTextDocument } from '../workspaceContents';
import { MdReferencesProvider } from './references';
import { MdReferencesComputer } from './references';
export class MdDefinitionProvider extends Disposable implements vscode.DefinitionProvider {
constructor(private readonly referencesProvider: MdReferencesProvider) {
constructor(
private readonly referencesComputer: MdReferencesComputer
) {
super();
}
async provideDefinition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Definition | undefined> {
const allRefs = await this.referencesProvider.getAllReferencesAtPosition(document, position, token);
const allRefs = await this.referencesComputer.getReferencesAtPosition(document, position, token);
return allRefs.find(ref => ref.kind === 'link' && ref.isDefinition)?.location;
}

View file

@ -18,7 +18,7 @@ import { ResourceMap } from '../util/resourceMap';
import { MdTableOfContentsWatcher } from '../test/tableOfContentsWatcher';
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
import { InternalHref, LinkDefinitionSet, MdLink, MdLinkComputer, MdLinkSource } from './documentLinkProvider';
import { MdReferencesProvider, tryFindMdDocumentForLink } from './references';
import { MdReferencesComputer, tryFindMdDocumentForLink } from './references';
const localize = nls.loadMessageBundle();
@ -311,7 +311,7 @@ export class DiagnosticManager extends Disposable {
private readonly computer: DiagnosticComputer,
private readonly configuration: DiagnosticConfiguration,
private readonly reporter: DiagnosticReporter,
private readonly referencesProvider: MdReferencesProvider,
private readonly referencesComputer: MdReferencesComputer,
delay = 300,
) {
super();
@ -350,7 +350,7 @@ export class DiagnosticManager extends Disposable {
this._register(this.tableOfContentsWatcher.onTocChanged(async e => {
// When the toc of a document changes, revalidate every file that linked to it too
const triggered = new ResourceMap<void>();
for (const ref of await this.referencesProvider.getAllReferencesToFile(e.uri, noopToken)) {
for (const ref of await this.referencesComputer.getAllReferencesToFile(e.uri, noopToken)) {
const file = ref.location.uri;
if (!triggered.has(file)) {
this.triggerDiagnostics(file);
@ -627,7 +627,7 @@ export function register(
workspaceContents: MdWorkspaceContents,
linkComputer: MdLinkComputer,
commandManager: CommandManager,
referenceProvider: MdReferencesProvider,
referenceComputer: MdReferencesComputer,
): vscode.Disposable {
const configuration = new VSCodeDiagnosticConfiguration();
const manager = new DiagnosticManager(
@ -636,7 +636,7 @@ export function register(
new DiagnosticComputer(engine, workspaceContents, linkComputer),
configuration,
new DiagnosticCollectionReporter(),
referenceProvider);
referenceComputer);
return vscode.Disposable.from(
configuration,
manager,

View file

@ -6,7 +6,7 @@
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { Command, CommandManager } from '../commandManager';
import { MdReferencesProvider } from './references';
import { MdReferencesComputer } from './references';
const localize = nls.loadMessageBundle();
@ -16,7 +16,7 @@ export class FindFileReferencesCommand implements Command {
public readonly id = 'markdown.findAllFileReferences';
constructor(
private readonly referencesProvider: MdReferencesProvider,
private readonly referencesComputer: MdReferencesComputer,
) { }
public async execute(resource?: vscode.Uri) {
@ -33,7 +33,7 @@ export class FindFileReferencesCommand implements Command {
location: vscode.ProgressLocation.Window,
title: localize('progress.title', "Finding file references")
}, async (_progress, token) => {
const references = await this.referencesProvider.getAllReferencesToFile(resource!, token);
const references = await this.referencesComputer.getAllReferencesToFile(resource!, token);
const locations = references.map(ref => ref.location);
const config = vscode.workspace.getConfiguration('references');
@ -49,6 +49,6 @@ export class FindFileReferencesCommand implements Command {
}
}
export function registerFindFileReferences(commandManager: CommandManager, referencesProvider: MdReferencesProvider): vscode.Disposable {
export function registerFindFileReferences(commandManager: CommandManager, referencesProvider: MdReferencesComputer): vscode.Disposable {
return commandManager.register(new FindFileReferencesCommand(referencesProvider));
}

View file

@ -59,7 +59,7 @@ export interface MdHeaderReference {
export type MdReference = MdLinkReference | MdHeaderReference;
export class MdReferencesProvider extends Disposable implements vscode.ReferenceProvider {
export class MdReferencesComputer extends Disposable {
private readonly _linkCache: MdWorkspaceCache<readonly MdLink[]>;
@ -74,15 +74,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
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> {
const allRefs = await this.getAllReferencesAtPosition(document, position, token);
return allRefs
.filter(ref => context.includeDeclaration || !ref.isDefinition)
.map(ref => ref.location);
}
public async getAllReferencesAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<MdReference[]> {
public async getReferencesAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<MdReference[]> {
const toc = await TableOfContents.create(this.engine, document);
if (token.isCancellationRequested) {
return [];
@ -96,6 +88,11 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
}
}
public async getAllReferencesToFile(resource: vscode.Uri, _token: vscode.CancellationToken): Promise<MdReference[]> {
const allLinksInWorkspace = (await this._linkCache.values()).flat();
return Array.from(this.findAllLinksToFile(resource, allLinksInWorkspace, undefined));
}
private async getReferencesToHeader(document: SkinnyTextDocument, header: TocEntry): Promise<MdReference[]> {
const links = (await this._linkCache.values()).flat();
@ -226,12 +223,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|| uri.Utils.extname(href.path) === '' && href.path.with({ path: href.path.path + '.md' }).fsPath === targetDoc.fsPath;
}
public async getAllReferencesToFile(resource: vscode.Uri, _token: vscode.CancellationToken): Promise<MdReference[]> {
const allLinksInWorkspace = (await this._linkCache.values()).flat();
return Array.from(this.findAllLinksToFile(resource, allLinksInWorkspace, undefined));
}
private * findAllLinksToFile(resource: vscode.Uri, allLinksInWorkspace: readonly MdLink[], sourceLink: MdLink | undefined): Iterable<MdReference> {
private *findAllLinksToFile(resource: vscode.Uri, allLinksInWorkspace: readonly MdLink[], sourceLink: MdLink | undefined): Iterable<MdReference> {
for (const link of allLinksInWorkspace) {
if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, resource)) {
continue;
@ -254,7 +246,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
}
}
private * getReferencesToLinkReference(allLinks: Iterable<MdLink>, refToFind: string, from: { resource: vscode.Uri; range: vscode.Range }): Iterable<MdReference> {
private *getReferencesToLinkReference(allLinks: Iterable<MdLink>, refToFind: string, from: { resource: vscode.Uri; range: vscode.Range }): Iterable<MdReference> {
for (const link of allLinks) {
let ref: string;
if (link.kind === 'definition') {
@ -291,6 +283,30 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
}
}
/**
*
*/
export class MdVsCodeReferencesProvider implements vscode.ReferenceProvider {
public constructor(
private readonly referencesComputer: MdReferencesComputer
) { }
async provideReferences(document: SkinnyTextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise<vscode.Location[]> {
const allRefs = await this.referencesComputer.getReferencesAtPosition(document, position, token);
return allRefs
.filter(ref => context.includeDeclaration || !ref.isDefinition)
.map(ref => ref.location);
}
}
export function registerReferencesProvider(
selector: vscode.DocumentSelector,
computer: MdReferencesComputer,
): vscode.Disposable {
return vscode.languages.registerReferenceProvider(selector, new MdVsCodeReferencesProvider(computer));
}
export async function tryFindMdDocumentForLink(href: InternalHref, workspaceContents: MdWorkspaceContents): Promise<SkinnyTextDocument | undefined> {
const targetDoc = await workspaceContents.getMarkdownDocument(href.path);
if (targetDoc) {
@ -305,4 +321,3 @@ export async function tryFindMdDocumentForLink(href: InternalHref, workspaceCont
return undefined;
}

View file

@ -11,7 +11,7 @@ import { Disposable } from '../util/dispose';
import { resolveDocumentLink } from '../util/openDocumentLink';
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
import { InternalHref } from './documentLinkProvider';
import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesProvider, tryFindMdDocumentForLink } from './references';
import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesComputer, tryFindMdDocumentForLink } from './references';
const localize = nls.loadMessageBundle();
@ -58,7 +58,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide
private readonly renameNotSupportedText = localize('invalidRenameLocation', "Rename not supported at location");
public constructor(
private readonly referencesProvider: MdReferencesProvider,
private readonly referencesComputer: MdReferencesComputer,
private readonly workspaceContents: MdWorkspaceContents,
private readonly slugifier: Slugifier,
) {
@ -253,7 +253,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide
return this.cachedRefs;
}
const references = await this.referencesProvider.getAllReferencesAtPosition(document, position, token);
const references = await this.referencesComputer.getReferencesAtPosition(document, position, token);
const triggerRef = references.find(ref => ref.isTriggerLocation);
if (!triggerRef) {
return undefined;

View file

@ -8,7 +8,7 @@ import 'mocha';
import * as vscode from 'vscode';
import { MdDefinitionProvider } from '../languageFeatures/definitionProvider';
import { MdLinkComputer } from '../languageFeatures/documentLinkProvider';
import { MdReferencesProvider } from '../languageFeatures/references';
import { MdReferencesComputer } from '../languageFeatures/references';
import { githubSlugifier } from '../slugify';
import { noopToken } from '../util/cancellation';
import { InMemoryDocument } from '../util/inMemoryDocument';
@ -21,8 +21,8 @@ import { joinLines, workspacePath } from './util';
function getDefinition(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) {
const engine = createNewMarkdownEngine();
const linkComputer = new MdLinkComputer(engine);
const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier);
const provider = new MdDefinitionProvider(referencesProvider);
const referencesComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier);
const provider = new MdDefinitionProvider(referencesComputer);
return provider.provideDefinition(doc, pos, noopToken);
}

View file

@ -8,7 +8,7 @@ import 'mocha';
import * as vscode from 'vscode';
import { DiagnosticCollectionReporter, DiagnosticComputer, DiagnosticConfiguration, DiagnosticLevel, DiagnosticManager, DiagnosticOptions, DiagnosticReporter } from '../languageFeatures/diagnostics';
import { MdLinkComputer } from '../languageFeatures/documentLinkProvider';
import { MdReferencesProvider } from '../languageFeatures/references';
import { MdReferencesComputer } from '../languageFeatures/references';
import { githubSlugifier } from '../slugify';
import { noopToken } from '../util/cancellation';
import { InMemoryDocument } from '../util/inMemoryDocument';
@ -42,14 +42,14 @@ function createDiagnosticsManager(
) {
const engine = createNewMarkdownEngine();
const linkComputer = new MdLinkComputer(engine);
const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier);
const referencesComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier);
return new DiagnosticManager(
engine,
workspaceContents,
new DiagnosticComputer(engine, workspaceContents, linkComputer),
configuration,
reporter,
referencesProvider,
referencesComputer,
0);
}

View file

@ -7,7 +7,7 @@ import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import { MdLinkComputer } from '../languageFeatures/documentLinkProvider';
import { MdReference, MdReferencesProvider } from '../languageFeatures/references';
import { MdReference, MdReferencesComputer } from '../languageFeatures/references';
import { githubSlugifier } from '../slugify';
import { noopToken } from '../util/cancellation';
import { InMemoryDocument } from '../util/inMemoryDocument';
@ -20,8 +20,8 @@ import { joinLines, workspacePath } from './util';
function getFileReferences(resource: vscode.Uri, workspaceContents: MdWorkspaceContents) {
const engine = createNewMarkdownEngine();
const linkComputer = new MdLinkComputer(engine);
const provider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier);
return provider.getAllReferencesToFile(resource, noopToken);
const computer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier);
return computer.getAllReferencesToFile(resource, noopToken);
}
function assertReferencesEqual(actualRefs: readonly MdReference[], ...expectedRefs: { uri: vscode.Uri; line: number }[]) {

View file

@ -7,7 +7,7 @@ import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import { MdLinkComputer } from '../languageFeatures/documentLinkProvider';
import { MdReferencesProvider } from '../languageFeatures/references';
import { MdReferencesComputer, MdVsCodeReferencesProvider } from '../languageFeatures/references';
import { githubSlugifier } from '../slugify';
import { noopToken } from '../util/cancellation';
import { InMemoryDocument } from '../util/inMemoryDocument';
@ -20,7 +20,8 @@ import { joinLines, workspacePath } from './util';
function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) {
const engine = createNewMarkdownEngine();
const linkComputer = new MdLinkComputer(engine);
const provider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier);
const computer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier);
const provider = new MdVsCodeReferencesProvider(computer);
return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken);
}

View file

@ -7,7 +7,7 @@ import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import { MdLinkComputer } from '../languageFeatures/documentLinkProvider';
import { MdReferencesProvider } from '../languageFeatures/references';
import { MdReferencesComputer } from '../languageFeatures/references';
import { MdRenameProvider, MdWorkspaceEdit } from '../languageFeatures/rename';
import { githubSlugifier } from '../slugify';
import { noopToken } from '../util/cancellation';
@ -24,8 +24,8 @@ import { assertRangeEqual, joinLines, workspacePath } from './util';
function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents): Promise<undefined | { readonly range: vscode.Range; readonly placeholder: string }> {
const engine = createNewMarkdownEngine();
const linkComputer = new MdLinkComputer(engine);
const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier);
const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier);
const referenceComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier);
const renameProvider = new MdRenameProvider(referenceComputer, workspaceContents, githubSlugifier);
return renameProvider.prepareRename(doc, pos, noopToken);
}
@ -35,7 +35,7 @@ function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspaceCon
function getRenameEdits(doc: InMemoryDocument, pos: vscode.Position, newName: string, workspaceContents: MdWorkspaceContents): Promise<MdWorkspaceEdit | undefined> {
const engine = createNewMarkdownEngine();
const linkComputer = new MdLinkComputer(engine);
const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier);
const referencesProvider = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier);
const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier);
return renameProvider.provideRenameEditsImpl(doc, pos, newName, noopToken);
}