Skip to content

Commit

Permalink
fix: reduce reloading when opening a cspell config (#2986)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason3S authored Dec 25, 2023
1 parent 37f3822 commit a8a1e18
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 25 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"contributes": {
"virtualWorkspaces": {
"supported": "limited",
"description": "In virtual workspaces, it is not possible to load the cspell configuration from a JavaScript file. The configuration must be in a JSON, JSONC, or YAML file. Any configuration that relies upon `node_modules` will not be loaded."
"description": "In virtual workspaces, it is not possible to load the CSpell configuration from a JavaScript file. The configuration must be in a JSON, JSONC, or YAML file. Any configuration that relies upon `node_modules` will not be loaded."
},
"untrustedWorkspaces": {
"supported": false
Expand Down
27 changes: 26 additions & 1 deletion packages/_server/src/config/documentSettings.mts
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,15 @@ const fileConfigsToImport = '.vscode.configs.to.import.cspell.config.json';
const fileConfigLocalImport = '.vscode.configs.import.cspell.config.json';
const fileVSCodeSettings = '.vscode.folder.settings.json';

const holdSettingsForMs = 1000;

export class DocumentSettings {
// Cache per folder settings
private valuesToClearOnReset: ClearFn[] = [];
private readonly fetchSettingsForUri = this.createCache((docUri: string | undefined) => this._fetchSettingsForUri(docUri));
private readonly fetchVSCodeConfiguration = this.createCache((uri?: string) => this._fetchVSCodeConfiguration(uri));
private readonly fetchRepoRootForDir = this.createCache((dir: FsPath) => findRepoRoot(dir));
private readonly pendingUrisToRelease = new Map<string, NodeJS.Timeout>();
public readonly fetchWorkspaceConfiguration = this.createCache((docUri: DocumentUri) => this._fetchWorkspaceConfiguration(docUri));
private readonly _folders = this.createLazy(() => this.fetchFolders());
readonly configsToImport = new Set<string>();
Expand All @@ -132,14 +135,29 @@ export class DocumentSettings {
readonly defaultSettings: CSpellUserSettings | Promise<CSpellUserSettings> = _defaultSettings,
) {}

async getSettings(document: TextDocumentUri): Promise<CSpellUserSettings> {
getSettings(document: TextDocumentUri): Promise<CSpellUserSettings> {
return this.getUriSettings(document.uri);
}

getUriSettings(uri: string | undefined): Promise<CSpellUserSettings> {
return this.fetchUriSettings(uri);
}

releaseUriSettings(uri: string): void {
log(`releaseUriSettings ${uri}`);
const pending = this.pendingUrisToRelease.get(uri);
if (pending !== undefined) return;

this.pendingUrisToRelease.set(
uri,
setTimeout(() => {
log(`releasedUriSettings ${uri}`);
this.pendingUrisToRelease.delete(uri);
this.fetchSettingsForUri.delete(uri);
}, holdSettingsForMs),
);
}

async calcIncludeExclude(uri: Uri): Promise<ExcludeIncludeIgnoreInfo> {
const _uri = handleSpecialUri(uri);
const settings = await this.fetchSettingsForUri(_uri.toString());
Expand Down Expand Up @@ -245,6 +263,13 @@ export class DocumentSettings {
}

private async fetchUriSettings(uri: string | undefined): Promise<CSpellUserSettings> {
if (uri) {
const pendingRelease = this.pendingUrisToRelease.get(uri);
if (pendingRelease !== undefined) {
clearTimeout(pendingRelease);
this.pendingUrisToRelease.delete(uri);
}
}
const exSettings = await this.fetchUriSettingsEx(uri);
return exSettings.settings;
}
Expand Down
9 changes: 4 additions & 5 deletions packages/_server/src/server.mts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,10 @@ import { debounce as simpleDebounce } from './utils/debounce.mjs';
import { textToWords } from './utils/index.mjs';
import { createPrecisionLogger } from './utils/logging.mjs';
import * as Validator from './validator.mjs';
import { CSpellFileSystemProvider } from './vfs/CSpellFileSystemProvider.mjs';
import { bindFileSystemProvider } from './vfs/CSpellFileSystemProvider.mjs';

log('Starting Spell Checker Server');

const tds = CSpell;

const defaultCheckLimit = Validator.defaultCheckLimit;

const overRideDefaults: CSpellUserSettings = {
Expand Down Expand Up @@ -127,7 +125,7 @@ export function run(): void {
),
);

CSpell.getVirtualFS().registerFileSystemProvider(new CSpellFileSystemProvider(clientServerApi, documents));
dd(bindFileSystemProvider(clientServerApi, documents));

const documentSettings = new DocumentSettings(connection, clientServerApi, defaultSettings);

Expand Down Expand Up @@ -272,6 +270,7 @@ export function run(): void {
validationByDoc.delete(uri);
sub.unsubscribe();
}
documentSettings.releaseUriSettings(uri);
// A text document was closed we clear the diagnostics
catchPromise(connection.sendDiagnostics({ uri, diagnostics: [] }), 'onDidClose');
}),
Expand Down Expand Up @@ -518,7 +517,7 @@ export function run(): void {
}

async function getSettingsToUseForDocument(doc: TextDocument) {
return tds.constructSettingsForText(await getBaseSettings(doc), doc.getText(), doc.languageId);
return CSpell.constructSettingsForText(await getBaseSettings(doc), doc.getText(), doc.languageId);
}

function isStale(doc: TextDocument, writeLog = true): boolean {
Expand Down
23 changes: 17 additions & 6 deletions packages/_server/src/vfs/CSpellFileSystemProvider.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,37 @@ import { logDebug } from '@internal/common-utils/log';
import type { VProviderFileSystem } from 'cspell-io';
import { FSCapabilityFlags, urlOrReferenceToUrl, VFileType } from 'cspell-io';
import type { VFileSystemProvider } from 'cspell-lib';
import type { TextDocuments } from 'vscode-languageserver/node.js';
import { getVirtualFS } from 'cspell-lib';
import type { Disposable, TextDocuments } from 'vscode-languageserver/node.js';
import type { TextDocument } from 'vscode-languageserver-textdocument';

import type { ServerSideApi } from '../api.js';
import { FileType } from '../api.js';

const UseCSpellForProtocol: Record<string, boolean> = {
const debugFileProtocol = false;

const NotHandledProtocols: Record<string, boolean> = {
'http:': true,
'https:': true,
'file:': true, // Use the cspell-io file system provider for performance.
'file:': !debugFileProtocol, // Use the cspell-io file system provider for performance.
};

export class CSpellFileSystemProvider implements VFileSystemProvider {
class CSpellFileSystemProvider implements VFileSystemProvider {
readonly name = 'VSCode';
constructor(
private api: ServerSideApi,
private documents: TextDocuments<TextDocument>,
) {}

getFileSystem(url: URL): VProviderFileSystem | undefined {
if (UseCSpellForProtocol[url.protocol.toLowerCase()]) return undefined;
if (NotHandledProtocols[url.protocol.toLowerCase()]) return undefined;

const vfs: VProviderFileSystem = {
capabilities: FSCapabilityFlags.Read | FSCapabilityFlags.Stat | FSCapabilityFlags.ReadDir,
stat: async (urlRef) => {
const url = urlOrReferenceToUrl(urlRef);
logDebug(`stat req: ${url.href}`);
const stat = await this.api.clientRequest.vfsStat(url.href);
logDebug(`stat res: ${url.href}\n \t${JSON.stringify(stat)}`);

return {
size: stat.size,
Expand Down Expand Up @@ -81,6 +83,15 @@ export class CSpellFileSystemProvider implements VFileSystemProvider {
}
}

export function bindFileSystemProvider(api: ServerSideApi, documents: TextDocuments<TextDocument>): Disposable {
const provider = new CSpellFileSystemProvider(api, documents);
const vfs = getVirtualFS();
if (debugFileProtocol) {
vfs.enableLogging(true);
}
return vfs.registerFileSystemProvider(provider);
}

export class VFSError extends Error {
constructor(
message: string,
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ export async function activate(context: ExtensionContext): Promise<ExtensionApi>
detectPossibleCSpellConfigChange(event.files);
}

function handleOpenFile(doc: vscode.TextDocument) {
detectPossibleCSpellConfigChange([doc.uri]);
function handleOpenFile(_doc: vscode.TextDocument) {
// detectPossibleCSpellConfigChange([doc.uri]);
}

function handleOnDidChangeActiveTextEditor(e?: vscode.TextEditor) {
Expand Down
21 changes: 11 additions & 10 deletions packages/client/src/settings/CSpellSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const configFileLocations = [
// Dynamic config is looked for last
'cspell.config.js',
'cspell.config.cjs',
'cspell.config.mjs',
// .config
'.config/.cspell.json',
'.config/cspell.json',
Expand All @@ -51,25 +52,25 @@ export const configFileLocations = [
'.config/cspell.config.cjs',
] as const;

export const configFileLocationGlob = `**/{${configFileLocations.join(',')}}`;
const setOfConfigFilesNames = new Set(configFileLocations.map((filename) => filename.split('/').slice(-1)[0]));

/**
* A set of files that if changed, could indicate that the cspell configuration changed.
*
* An alias of possibleConfigFiles
*/
export const configFilesToWatch: Set<string> = Object.freeze(setOfConfigFilesNames);

export const configFileLocationGlob = `**/{${[...setOfConfigFilesNames].join(',')}}`;

type ConfigFileNames = (typeof configFileLocations)[number];

export const nestedConfigLocations = ['package.json'];

export const cspellConfigDirectory = '.cspell';

export const possibleConfigFiles = Object.freeze(new Set(configFileLocations));

export const preferredConfigFiles: ConfigFileNames[] = ['cspell.json', 'cspell.config.yaml', 'package.json'];

/**
* A set of files that if changed, could indicate that the cspell configuration changed.
*
* An alias of possibleConfigFiles
*/
export const configFilesToWatch = possibleConfigFiles as Set<string>;

export type CSpellSettings = CSpellUserSettings;

const defaultSettings: CSpellSettings = Object.freeze({
Expand Down

0 comments on commit a8a1e18

Please sign in to comment.