Add resourceMap helper for markdown extension (#151471)

This change introduces a `ResoruceMap` map type that is essentially `Map<vscode.Uri, T>`

It also fixes a potential race condition with `MdWorkspaceCache` where two quick calls would both trigger init
This commit is contained in:
Matt Bierner 2022-06-07 18:00:10 -07:00 committed by GitHub
parent 5bbab47c7c
commit 60a68d666d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 21 deletions

View file

@ -97,7 +97,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
}
private async getReferencesToHeader(document: SkinnyTextDocument, header: TocEntry): Promise<MdReference[]> {
const links = (await this._linkCache.getAll()).flat();
const links = (await this._linkCache.values()).flat();
const references: MdReference[] = [];
@ -150,7 +150,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
}
private async getReferencesToLink(sourceLink: MdLink, triggerPosition: vscode.Position, token: vscode.CancellationToken): Promise<MdReference[]> {
const allLinksInWorkspace = (await this._linkCache.getAll()).flat();
const allLinksInWorkspace = (await this._linkCache.values()).flat();
if (token.isCancellationRequested) {
return [];
}
@ -227,7 +227,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
}
public async getAllReferencesToFile(resource: vscode.Uri, _token: vscode.CancellationToken): Promise<MdReference[]> {
const allLinksInWorkspace = (await this._linkCache.getAll()).flat();
const allLinksInWorkspace = (await this._linkCache.values()).flat();
return Array.from(this.findAllLinksToFile(resource, allLinksInWorkspace, undefined));
}

View file

@ -6,6 +6,7 @@
import * as vscode from 'vscode';
import { Disposable } from '../util/dispose';
import { Lazy, lazy } from '../util/lazy';
import { ResourceMap } from '../util/resourceMap';
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
/**
@ -13,8 +14,8 @@ import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
*/
export class MdWorkspaceCache<T> extends Disposable {
private readonly _cache = new Map<string, Lazy<Promise<T>>>();
private _hasPopulatedCache = false;
private readonly _cache = new ResourceMap<Lazy<Promise<T>>>();
private _init?: Promise<void>;
public constructor(
private readonly workspaceContents: MdWorkspaceContents,
@ -23,19 +24,29 @@ export class MdWorkspaceCache<T> extends Disposable {
super();
}
public async getAll(): Promise<T[]> {
if (!this._hasPopulatedCache) {
await this.populateCache();
this._hasPopulatedCache = true;
this.workspaceContents.onDidChangeMarkdownDocument(this.onDidChangeDocument, this, this._disposables);
this.workspaceContents.onDidCreateMarkdownDocument(this.onDidChangeDocument, this, this._disposables);
this.workspaceContents.onDidDeleteMarkdownDocument(this.onDidDeleteDocument, this, this._disposables);
}
public async entries(): Promise<Array<[vscode.Uri, T]>> {
await this.ensureInit();
return Promise.all(Array.from(this._cache.entries(), async ([key, entry]) => {
return [key, await entry.value];
}));
}
public async values(): Promise<Array<T>> {
await this.ensureInit();
return Promise.all(Array.from(this._cache.values(), x => x.value));
}
private async ensureInit(): Promise<void> {
if (!this._init) {
this._init = this.populateCache();
this._register(this.workspaceContents.onDidChangeMarkdownDocument(this.onDidChangeDocument, this));
this._register(this.workspaceContents.onDidCreateMarkdownDocument(this.onDidChangeDocument, this));
this._register(this.workspaceContents.onDidDeleteMarkdownDocument(this.onDidDeleteDocument, this));
}
await this._init;
}
private async populateCache(): Promise<void> {
const markdownDocumentUris = await this.workspaceContents.getAllMarkdownDocuments();
for (const document of markdownDocumentUris) {
@ -43,12 +54,8 @@ export class MdWorkspaceCache<T> extends Disposable {
}
}
private key(resource: vscode.Uri): string {
return resource.toString();
}
private update(document: SkinnyTextDocument): void {
this._cache.set(this.key(document.uri), lazy(() => this.getValue(document)));
this._cache.set(document.uri, lazy(() => this.getValue(document)));
}
private onDidChangeDocument(document: SkinnyTextDocument) {
@ -56,6 +63,6 @@ export class MdWorkspaceCache<T> extends Disposable {
}
private onDidDeleteDocument(resource: vscode.Uri) {
this._cache.delete(this.key(resource));
this._cache.delete(resource);
}
}

View file

@ -23,7 +23,7 @@ export class MdWorkspaceSymbolProvider extends Disposable implements vscode.Work
}
public async provideWorkspaceSymbols(query: string): Promise<vscode.SymbolInformation[]> {
const allSymbols = (await this._cache.getAll()).flat();
const allSymbols = (await this._cache.values()).flat();
return allSymbols.filter(symbolInformation => symbolInformation.name.toLowerCase().indexOf(query.toLowerCase()) !== -1);
}
}

View file

@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
export class ResourceMap<T> {
private readonly map = new Map<string, { readonly uri: vscode.Uri; readonly value: T }>();
public set(uri: vscode.Uri, value: T): this {
this.map.set(this.toKey(uri), { uri, value });
return this;
}
public get(resource: vscode.Uri): T | undefined {
return this.map.get(this.toKey(resource))?.value;
}
public has(resource: vscode.Uri): boolean {
return this.map.has(this.toKey(resource));
}
public get size(): number {
return this.map.size;
}
public clear(): void {
this.map.clear();
}
public delete(resource: vscode.Uri): boolean {
return this.map.delete(this.toKey(resource));
}
public *values(): IterableIterator<T> {
for (const entry of this.map.values()) {
yield entry.value;
}
}
public *keys(): IterableIterator<vscode.Uri> {
for (const entry of this.map.values()) {
yield entry.uri;
}
}
public *entries(): IterableIterator<[vscode.Uri, T]> {
for (const entry of this.map.values()) {
yield [entry.uri, entry.value];
}
}
public [Symbol.iterator](): IterableIterator<[vscode.Uri, T]> {
return this.entries();
}
private toKey(resource: vscode.Uri) {
return resource.toString();
}
}