From 22e7676a8c05c95ff451fe2306bebf5ed2421b4d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 11 May 2021 14:27:47 +0200 Subject: [PATCH] Add `DiagnosticsOptions.onlyVisible` to limit computing diagnostics to only the visible text models --- monaco.d.ts | 5 ++++ src/languageFeatures.ts | 50 ++++++++++++++++++++++++++++++++------ src/monaco.contribution.ts | 5 ++-- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/monaco.d.ts b/monaco.d.ts index feb49fc4..1021c98f 100644 --- a/monaco.d.ts +++ b/monaco.d.ts @@ -143,6 +143,11 @@ declare namespace monaco.languages.typescript { noSemanticValidation?: boolean; noSyntaxValidation?: boolean; noSuggestionDiagnostics?: boolean; + /** + * Limit diagnostic computation to only visible files. + * Defaults to false. + */ + onlyVisible?: boolean; diagnosticCodesToIgnore?: number[]; } export interface WorkerOptions { diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index abf50136..d54c7a1a 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -163,6 +163,15 @@ enum DiagnosticCategory { Message = 3 } +/** + * temporary interface until the editor API exposes + * `IModel.isAttachedToEditor` and `IModel.onDidChangeAttached` + */ +interface IInternalEditorModel extends editor.IModel { + onDidChangeAttached(listener: () => void): IDisposable; + isAttachedToEditor(): boolean; +} + export class DiagnosticsAdapter extends Adapter { private _disposables: IDisposable[] = []; private _listener: { [uri: string]: IDisposable } = Object.create(null); @@ -175,25 +184,52 @@ export class DiagnosticsAdapter extends Adapter { ) { super(worker); - const onModelAdd = (model: editor.IModel): void => { + const onModelAdd = (model: IInternalEditorModel): void => { if (model.getModeId() !== _selector) { return; } + const maybeValidate = () => { + const { onlyVisible } = this._defaults.getDiagnosticsOptions(); + if (onlyVisible) { + if (model.isAttachedToEditor()) { + this._doValidate(model); + } + } else { + this._doValidate(model); + } + }; + let handle: number; const changeSubscription = model.onDidChangeContent(() => { clearTimeout(handle); - handle = setTimeout(() => this._doValidate(model), 500); + handle = setTimeout(maybeValidate, 500); + }); + + const visibleSubscription = model.onDidChangeAttached(() => { + const { onlyVisible } = this._defaults.getDiagnosticsOptions(); + if (onlyVisible) { + if (model.isAttachedToEditor()) { + // this model is now attached to an editor + // => compute diagnostics + maybeValidate(); + } else { + // this model is no longer attached to an editor + // => clear existing diagnostics + editor.setModelMarkers(model, this._selector, []); + } + } }); this._listener[model.uri.toString()] = { dispose() { changeSubscription.dispose(); + visibleSubscription.dispose(); clearTimeout(handle); } }; - this._doValidate(model); + maybeValidate(); }; const onModelRemoved = (model: editor.IModel): void => { @@ -205,12 +241,12 @@ export class DiagnosticsAdapter extends Adapter { } }; - this._disposables.push(editor.onDidCreateModel(onModelAdd)); + this._disposables.push(editor.onDidCreateModel((model) => onModelAdd(model))); this._disposables.push(editor.onWillDisposeModel(onModelRemoved)); this._disposables.push( editor.onDidChangeModelLanguage((event) => { onModelRemoved(event.model); - onModelAdd(event.model); + onModelAdd(event.model); }) ); @@ -226,13 +262,13 @@ export class DiagnosticsAdapter extends Adapter { // redo diagnostics when options change for (const model of editor.getModels()) { onModelRemoved(model); - onModelAdd(model); + onModelAdd(model); } }; this._disposables.push(this._defaults.onDidChange(recomputeDiagostics)); this._disposables.push(this._defaults.onDidExtraLibsChange(recomputeDiagostics)); - editor.getModels().forEach(onModelAdd); + editor.getModels().forEach((model) => onModelAdd(model)); } public dispose(): void { diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index 534676f3..d1f0ea3c 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -156,6 +156,7 @@ export interface DiagnosticsOptions { noSemanticValidation?: boolean; noSyntaxValidation?: boolean; noSuggestionDiagnostics?: boolean; + onlyVisible?: boolean; diagnosticCodesToIgnore?: number[]; } @@ -616,13 +617,13 @@ export const typescriptVersion: string = tsversion; export const typescriptDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl( { allowNonTsExtensions: true, target: ScriptTarget.Latest }, - { noSemanticValidation: false, noSyntaxValidation: false }, + { noSemanticValidation: false, noSyntaxValidation: false, onlyVisible: false }, {} ); export const javascriptDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl( { allowNonTsExtensions: true, allowJs: true, target: ScriptTarget.Latest }, - { noSemanticValidation: true, noSyntaxValidation: false }, + { noSemanticValidation: true, noSyntaxValidation: false, onlyVisible: false }, {} );