mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 04:49:35 +00:00
Only compute diagnostics for opened md files (#153395)
* Only compute diagnostics for opened md files For #152494 * Make tests stable for result ordering
This commit is contained in:
parent
87e684ef9b
commit
e13feea6ae
|
@ -242,7 +242,9 @@ export abstract class DiagnosticReporter extends Disposable {
|
|||
|
||||
public abstract delete(uri: vscode.Uri): void;
|
||||
|
||||
public abstract areDiagnosticsEnabled(uri: vscode.Uri): boolean;
|
||||
public abstract isOpen(uri: vscode.Uri): boolean;
|
||||
|
||||
public abstract getOpenDocuments(): ITextDocument[];
|
||||
|
||||
public addWorkItem(promise: Promise<any>): Promise<any> {
|
||||
this.pending.add(promise);
|
||||
|
@ -256,6 +258,7 @@ export abstract class DiagnosticReporter extends Disposable {
|
|||
}
|
||||
|
||||
export class DiagnosticCollectionReporter extends DiagnosticReporter {
|
||||
|
||||
private readonly collection: vscode.DiagnosticCollection;
|
||||
|
||||
constructor() {
|
||||
|
@ -269,11 +272,11 @@ export class DiagnosticCollectionReporter extends DiagnosticReporter {
|
|||
}
|
||||
|
||||
public set(uri: vscode.Uri, diagnostics: readonly vscode.Diagnostic[]): void {
|
||||
this.collection.set(uri, this.areDiagnosticsEnabled(uri) ? diagnostics : []);
|
||||
this.collection.set(uri, this.isOpen(uri) ? diagnostics : []);
|
||||
}
|
||||
|
||||
public areDiagnosticsEnabled(uri: vscode.Uri): boolean {
|
||||
const tabs = this.getAllTabResources();
|
||||
public isOpen(uri: vscode.Uri): boolean {
|
||||
const tabs = this.getTabResources();
|
||||
return tabs.has(uri);
|
||||
}
|
||||
|
||||
|
@ -281,7 +284,12 @@ export class DiagnosticCollectionReporter extends DiagnosticReporter {
|
|||
this.collection.delete(uri);
|
||||
}
|
||||
|
||||
private getAllTabResources(): ResourceMap<void> {
|
||||
public getOpenDocuments(): ITextDocument[] {
|
||||
const tabs = this.getTabResources();
|
||||
return vscode.workspace.textDocuments.filter(doc => tabs.has(doc.uri));
|
||||
}
|
||||
|
||||
private getTabResources(): ResourceMap<void> {
|
||||
const openedTabDocs = new ResourceMap<void>();
|
||||
for (const group of vscode.window.tabGroups.all) {
|
||||
for (const tab of group.tabs) {
|
||||
|
@ -365,7 +373,7 @@ export class DiagnosticManager extends Disposable {
|
|||
return this.reporter.addWorkItem(
|
||||
(async () => {
|
||||
const triggered = new ResourceMap<Promise<void>>();
|
||||
for (const ref of await this.referencesProvider.getAllReferencesToFile(uri, noopToken)) {
|
||||
for (const ref of await this.referencesProvider.getReferencesToFileInDocs(uri, this.reporter.getOpenDocuments(), noopToken)) {
|
||||
const file = ref.location.uri;
|
||||
if (!triggered.has(file)) {
|
||||
triggered.set(file, this.triggerDiagnostics(file));
|
||||
|
@ -398,7 +406,7 @@ export class DiagnosticManager extends Disposable {
|
|||
const doc = await this.workspace.getOrLoadMarkdownDocument(resource);
|
||||
if (doc) {
|
||||
await this.inFlightDiagnostics.trigger(doc.uri, async (token) => {
|
||||
if (this.reporter.areDiagnosticsEnabled(doc.uri)) {
|
||||
if (this.reporter.isOpen(doc.uri)) {
|
||||
const state = await this.recomputeDiagnosticState(doc, token);
|
||||
this.linkWatcher.updateLinksForDocument(doc.uri, state.config.enabled && state.config.validateFileLinks ? state.links : []);
|
||||
this.reporter.set(doc.uri, state.diagnostics);
|
||||
|
@ -417,12 +425,7 @@ export class DiagnosticManager extends Disposable {
|
|||
this.inFlightDiagnostics.clear();
|
||||
|
||||
return this.reporter.addWorkItem(
|
||||
(async () => {
|
||||
// TODO: This pulls in all md files in the workspace. Instead we only care about opened text documents.
|
||||
// Need a new way to handle that.
|
||||
const allDocs = await this.workspace.getAllMarkdownDocuments();
|
||||
await Promise.all(Array.from(allDocs, doc => this.triggerDiagnostics(doc.uri)));
|
||||
})()
|
||||
Promise.all(Array.from(this.reporter.getOpenDocuments(), doc => this.triggerDiagnostics(doc.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.referencesProvider.getReferencesToFileInWorkspace(resource!, token);
|
||||
const locations = references.map(ref => ref.location);
|
||||
|
||||
const config = vscode.workspace.getConfiguration('references');
|
||||
|
|
|
@ -67,7 +67,6 @@ export type MdReference = MdLinkReference | MdHeaderReference;
|
|||
export class MdReferencesProvider extends Disposable {
|
||||
|
||||
private readonly _linkCache: MdWorkspaceInfoCache<readonly MdLink[]>;
|
||||
private readonly _linkComputer: MdLinkComputer;
|
||||
|
||||
public constructor(
|
||||
private readonly parser: IMdParser,
|
||||
|
@ -77,8 +76,8 @@ export class MdReferencesProvider extends Disposable {
|
|||
) {
|
||||
super();
|
||||
|
||||
this._linkComputer = new MdLinkComputer(parser);
|
||||
this._linkCache = this._register(new MdWorkspaceInfoCache(workspace, doc => this._linkComputer.getAllLinks(doc, noopToken)));
|
||||
const linkComputer = new MdLinkComputer(parser);
|
||||
this._linkCache = this._register(new MdWorkspaceInfoCache(workspace, doc => linkComputer.getAllLinks(doc, noopToken)));
|
||||
}
|
||||
|
||||
public async getReferencesAtPosition(document: ITextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<MdReference[]> {
|
||||
|
@ -97,11 +96,26 @@ export class MdReferencesProvider extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
public async getAllReferencesToFile(resource: vscode.Uri, _token: vscode.CancellationToken): Promise<MdReference[]> {
|
||||
this.logger.verbose('ReferencesProvider', `getAllReferencesToFile: ${resource}`);
|
||||
public async getReferencesToFileInWorkspace(resource: vscode.Uri, token: vscode.CancellationToken): Promise<MdReference[]> {
|
||||
this.logger.verbose('ReferencesProvider', `getAllReferencesToFileInWorkspace: ${resource}`);
|
||||
|
||||
const allLinksInWorkspace = (await this._linkCache.values()).flat();
|
||||
return Array.from(this.findAllLinksToFile(resource, allLinksInWorkspace, undefined));
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Array.from(this.findLinksToFile(resource, allLinksInWorkspace, undefined));
|
||||
}
|
||||
|
||||
public async getReferencesToFileInDocs(resource: vscode.Uri, otherDocs: readonly ITextDocument[], token: vscode.CancellationToken): Promise<MdReference[]> {
|
||||
this.logger.verbose('ReferencesProvider', `getAllReferencesToFileInFiles: ${resource}`);
|
||||
|
||||
const links = (await this._linkCache.getForDocs(otherDocs)).flat();
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Array.from(this.findLinksToFile(resource, links, undefined));
|
||||
}
|
||||
|
||||
private async getReferencesToHeader(document: ITextDocument, header: TocEntry): Promise<MdReference[]> {
|
||||
|
@ -137,7 +151,7 @@ export class MdReferencesProvider extends Disposable {
|
|||
}
|
||||
|
||||
private async getReferencesToLinkAtPosition(document: ITextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<MdReference[]> {
|
||||
const docLinks = await this._linkComputer.getAllLinks(document, token);
|
||||
const docLinks = (await this._linkCache.getForDocs([document]))[0];
|
||||
|
||||
for (const link of docLinks) {
|
||||
if (link.kind === 'definition') {
|
||||
|
@ -223,7 +237,7 @@ export class MdReferencesProvider extends Disposable {
|
|||
}
|
||||
}
|
||||
} else { // Triggered on a link without a fragment so we only require matching the file and ignore fragments
|
||||
references.push(...this.findAllLinksToFile(resolvedResource ?? sourceLink.href.path, allLinksInWorkspace, sourceLink));
|
||||
references.push(...this.findLinksToFile(resolvedResource ?? sourceLink.href.path, allLinksInWorkspace, sourceLink));
|
||||
}
|
||||
|
||||
return references;
|
||||
|
@ -238,8 +252,8 @@ export class MdReferencesProvider extends Disposable {
|
|||
|| uri.Utils.extname(href.path) === '' && href.path.with({ path: href.path.path + '.md' }).fsPath === targetDoc.fsPath;
|
||||
}
|
||||
|
||||
private *findAllLinksToFile(resource: vscode.Uri, allLinksInWorkspace: readonly MdLink[], sourceLink: MdLink | undefined): Iterable<MdReference> {
|
||||
for (const link of allLinksInWorkspace) {
|
||||
private *findLinksToFile(resource: vscode.Uri, links: readonly MdLink[], sourceLink: MdLink | undefined): Iterable<MdReference> {
|
||||
for (const link of links) {
|
||||
if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, resource)) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ export class VsCodeOutputLogger extends Disposable implements ILogger {
|
|||
const now = new Date();
|
||||
return String(now.getUTCHours()).padStart(2, '0')
|
||||
+ ':' + String(now.getMinutes()).padStart(2, '0')
|
||||
+ ':' + String(now.getUTCSeconds()).padStart(2, '0') + '.' + now.getMilliseconds();
|
||||
+ ':' + String(now.getUTCSeconds()).padStart(2, '0') + '.' + String(now.getMilliseconds()).padStart(3, '0');
|
||||
}
|
||||
|
||||
private updateConfiguration(): void {
|
||||
|
|
|
@ -10,6 +10,7 @@ import { DiagnosticCollectionReporter, DiagnosticComputer, DiagnosticConfigurati
|
|||
import { MdLinkProvider } from '../languageFeatures/documentLinks';
|
||||
import { MdReferencesProvider } from '../languageFeatures/references';
|
||||
import { MdTableOfContentsProvider } from '../tableOfContents';
|
||||
import { ITextDocument } from '../types/textDocument';
|
||||
import { noopToken } from '../util/cancellation';
|
||||
import { DisposableStore } from '../util/dispose';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
|
@ -79,6 +80,12 @@ class MemoryDiagnosticReporter extends DiagnosticReporter {
|
|||
|
||||
private readonly diagnostics = new ResourceMap<readonly vscode.Diagnostic[]>();
|
||||
|
||||
constructor(
|
||||
private readonly workspace: InMemoryMdWorkspace,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
super.clear();
|
||||
this.clear();
|
||||
|
@ -93,7 +100,7 @@ class MemoryDiagnosticReporter extends DiagnosticReporter {
|
|||
this.diagnostics.set(uri, diagnostics);
|
||||
}
|
||||
|
||||
areDiagnosticsEnabled(_uri: vscode.Uri): boolean {
|
||||
isOpen(_uri: vscode.Uri): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -104,6 +111,10 @@ class MemoryDiagnosticReporter extends DiagnosticReporter {
|
|||
get(uri: vscode.Uri): readonly vscode.Diagnostic[] {
|
||||
return orderDiagnosticsByRange(this.diagnostics.get(uri) ?? []);
|
||||
}
|
||||
|
||||
getOpenDocuments(): ITextDocument[] {
|
||||
return this.workspace.values();
|
||||
}
|
||||
}
|
||||
|
||||
suite('markdown: Diagnostic Computer', () => {
|
||||
|
@ -454,7 +465,7 @@ suite('Markdown: Diagnostics manager', () => {
|
|||
))
|
||||
]));
|
||||
|
||||
const reporter = store.add(new MemoryDiagnosticReporter());
|
||||
const reporter = store.add(new MemoryDiagnosticReporter(workspace));
|
||||
const config = new MemoryDiagnosticConfiguration({ enabled: true });
|
||||
|
||||
const manager = createDiagnosticsManager(store, workspace, config, reporter);
|
||||
|
@ -499,7 +510,7 @@ suite('Markdown: Diagnostics manager', () => {
|
|||
`[text](#no-such-2)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1, doc2]));
|
||||
const reporter = store.add(new MemoryDiagnosticReporter());
|
||||
const reporter = store.add(new MemoryDiagnosticReporter(workspace));
|
||||
|
||||
const manager = createDiagnosticsManager(store, workspace, new MemoryDiagnosticConfiguration({}), reporter);
|
||||
await manager.ready;
|
||||
|
@ -554,7 +565,7 @@ suite('Markdown: Diagnostics manager', () => {
|
|||
`# Header`
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1, doc2]));
|
||||
const reporter = store.add(new MemoryDiagnosticReporter());
|
||||
const reporter = store.add(new MemoryDiagnosticReporter(workspace));
|
||||
|
||||
const manager = createDiagnosticsManager(store, workspace, new MemoryDiagnosticConfiguration({}), reporter);
|
||||
await manager.ready;
|
||||
|
|
|
@ -22,7 +22,7 @@ function getFileReferences(store: DisposableStore, resource: vscode.Uri, workspa
|
|||
const engine = createNewMarkdownEngine();
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const computer = store.add(new MdReferencesProvider(engine, workspace, tocProvider, nulLogger));
|
||||
return computer.getAllReferencesToFile(resource, noopToken);
|
||||
return computer.getReferencesToFileInWorkspace(resource, noopToken);
|
||||
}
|
||||
|
||||
function assertReferencesEqual(actualRefs: readonly MdReference[], ...expectedRefs: { uri: vscode.Uri; line: number }[]) {
|
||||
|
|
|
@ -22,10 +22,14 @@ export class InMemoryMdWorkspace extends Disposable implements IMdWorkspace {
|
|||
}
|
||||
}
|
||||
|
||||
public async getAllMarkdownDocuments() {
|
||||
public values() {
|
||||
return Array.from(this._documents.values());
|
||||
}
|
||||
|
||||
public async getAllMarkdownDocuments() {
|
||||
return this.values();
|
||||
}
|
||||
|
||||
public async getOrLoadMarkdownDocument(resource: vscode.Uri): Promise<ITextDocument | undefined> {
|
||||
return this._documents.get(resource);
|
||||
}
|
||||
|
|
|
@ -18,12 +18,19 @@ import { nulLogger } from './nulLogging';
|
|||
import { joinLines, withStore, workspacePath } from './util';
|
||||
|
||||
|
||||
function getReferences(store: DisposableStore, doc: InMemoryDocument, pos: vscode.Position, workspace: IMdWorkspace) {
|
||||
async function getReferences(store: DisposableStore, doc: InMemoryDocument, pos: vscode.Position, workspace: IMdWorkspace) {
|
||||
const engine = createNewMarkdownEngine();
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const computer = store.add(new MdReferencesProvider(engine, workspace, tocProvider, nulLogger));
|
||||
const provider = new MdVsCodeReferencesProvider(computer);
|
||||
return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken);
|
||||
const refs = await provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken);
|
||||
return refs.sort((a, b) => {
|
||||
const pathCompare = a.uri.toString().localeCompare(b.uri.toString());
|
||||
if (pathCompare !== 0) {
|
||||
return pathCompare;
|
||||
}
|
||||
return a.range.start.compareTo(b.range.start);
|
||||
});
|
||||
}
|
||||
|
||||
function assertReferencesEqual(actualRefs: readonly vscode.Location[], ...expectedRefs: { uri: vscode.Uri; line: number; startCharacter?: number; endCharacter?: number }[]) {
|
||||
|
@ -130,7 +137,7 @@ suite('Markdown: Find all references', () => {
|
|||
test('Should find references from header across files', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const other1Uri = workspacePath('sub', 'other.md');
|
||||
const other2Uri = workspacePath('other2.md');
|
||||
const other2Uri = workspacePath('zOther2.md');
|
||||
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`# abc`,
|
||||
|
@ -216,7 +223,7 @@ suite('Markdown: Find all references', () => {
|
|||
test('Should find references from link across files', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const other1Uri = workspacePath('sub', 'other.md');
|
||||
const other2Uri = workspacePath('other2.md');
|
||||
const other2Uri = workspacePath('zOther2.md');
|
||||
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`# abc`,
|
||||
|
@ -300,9 +307,9 @@ suite('Markdown: Find all references', () => {
|
|||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 23), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: other1Uri, line: 1 }, // Header definition
|
||||
{ uri: docUri, line: 0 },
|
||||
{ uri: docUri, line: 1 },
|
||||
{ uri: other1Uri, line: 1 }, // Header definition
|
||||
);
|
||||
}));
|
||||
|
||||
|
@ -467,7 +474,7 @@ suite('Markdown: Find all references', () => {
|
|||
{
|
||||
// Check refs to header fragment
|
||||
const headerRefs = await getReferences(store, otherDoc, new vscode.Position(0, 16), workspace);
|
||||
assertReferencesEqual(headerRefs!,
|
||||
assertReferencesEqual(headerRefs,
|
||||
{ uri: docUri, line: 0 }, // Header definition
|
||||
{ uri: docUri, line: 2 },
|
||||
{ uri: other1Uri, line: 0 },
|
||||
|
@ -477,7 +484,7 @@ suite('Markdown: Find all references', () => {
|
|||
{
|
||||
// Check refs to file itself from link with ext
|
||||
const fileRefs = await getReferences(store, otherDoc, new vscode.Position(0, 9), workspace);
|
||||
assertReferencesEqual(fileRefs!,
|
||||
assertReferencesEqual(fileRefs,
|
||||
{ uri: other1Uri, line: 0, endCharacter: 14 },
|
||||
{ uri: other1Uri, line: 1, endCharacter: 19 },
|
||||
);
|
||||
|
@ -485,7 +492,7 @@ suite('Markdown: Find all references', () => {
|
|||
{
|
||||
// Check refs to file itself from link without ext
|
||||
const fileRefs = await getReferences(store, otherDoc, new vscode.Position(1, 17), workspace);
|
||||
assertReferencesEqual(fileRefs!,
|
||||
assertReferencesEqual(fileRefs,
|
||||
{ uri: other1Uri, line: 0 },
|
||||
{ uri: other1Uri, line: 1 },
|
||||
);
|
||||
|
|
|
@ -143,6 +143,16 @@ export class MdWorkspaceInfoCache<T> extends Disposable {
|
|||
return Array.from(await this._cache.entries(), x => x[1]);
|
||||
}
|
||||
|
||||
public async getForDocs(docs: readonly ITextDocument[]): Promise<T[]> {
|
||||
for (const doc of docs) {
|
||||
if (!this._cache.has(doc.uri)) {
|
||||
this.update(doc);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(docs.map(doc => this._cache.get(doc.uri) as Promise<T>));
|
||||
}
|
||||
|
||||
private async ensureInit(): Promise<void> {
|
||||
if (!this._init) {
|
||||
this._init = this.populateCache();
|
||||
|
@ -157,7 +167,9 @@ export class MdWorkspaceInfoCache<T> extends Disposable {
|
|||
private async populateCache(): Promise<void> {
|
||||
const markdownDocumentUris = await this.workspace.getAllMarkdownDocuments();
|
||||
for (const document of markdownDocumentUris) {
|
||||
this.update(document);
|
||||
if (!this._cache.has(document.uri)) {
|
||||
this.update(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue