From 0312af769bcbce1b98424e4a2548d39045bfb176 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 17 Nov 2021 10:32:04 +0100 Subject: [PATCH] Extract a common `DiagnosticsAdapter` --- src/common/lspLanguageFeatures.ts | 151 ++++++++++++++++++++++++++++++ src/css/cssMode.ts | 2 +- src/css/languageFeatures.ts | 125 +------------------------ src/json/jsonMode.ts | 2 +- src/json/languageFeatures.ts | 120 +----------------------- 5 files changed, 163 insertions(+), 237 deletions(-) create mode 100644 src/common/lspLanguageFeatures.ts diff --git a/src/common/lspLanguageFeatures.ts b/src/common/lspLanguageFeatures.ts new file mode 100644 index 00000000..d61dbeeb --- /dev/null +++ b/src/common/lspLanguageFeatures.ts @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as lsTypes from 'vscode-languageserver-types'; +import { + languages, + editor, + IMarkdownString, + Uri, + Position, + IRange, + Range, + CancellationToken, + IDisposable, + MarkerSeverity, + IEvent +} from '../fillers/monaco-editor-core'; + +export interface WorkerAccessor { + (...more: Uri[]): Promise; +} + +//#region DiagnosticsAdapter + +export interface ILanguageWorkerWithDiagnostics { + doValidation(uri: string): Promise; +} + +export class DiagnosticsAdapter { + protected _disposables: IDisposable[] = []; + private _listener: { [uri: string]: IDisposable } = Object.create(null); + + constructor( + private _languageId: string, + protected _worker: WorkerAccessor, + configChangeEvent: IEvent + ) { + const onModelAdd = (model: editor.IModel): void => { + let modeId = model.getLanguageId(); + if (modeId !== this._languageId) { + return; + } + + let handle: number; + this._listener[model.uri.toString()] = model.onDidChangeContent(() => { + window.clearTimeout(handle); + handle = window.setTimeout(() => this._doValidate(model.uri, modeId), 500); + }); + + this._doValidate(model.uri, modeId); + }; + + const onModelRemoved = (model: editor.IModel): void => { + editor.setModelMarkers(model, this._languageId, []); + + let uriStr = model.uri.toString(); + let listener = this._listener[uriStr]; + if (listener) { + listener.dispose(); + delete this._listener[uriStr]; + } + }; + + this._disposables.push(editor.onDidCreateModel(onModelAdd)); + this._disposables.push(editor.onWillDisposeModel(onModelRemoved)); + this._disposables.push( + editor.onDidChangeModelLanguage((event) => { + onModelRemoved(event.model); + onModelAdd(event.model); + }) + ); + + this._disposables.push( + configChangeEvent((_) => { + editor.getModels().forEach((model) => { + if (model.getLanguageId() === this._languageId) { + onModelRemoved(model); + onModelAdd(model); + } + }); + }) + ); + + this._disposables.push({ + dispose: () => { + editor.getModels().forEach(onModelRemoved); + for (let key in this._listener) { + this._listener[key].dispose(); + } + } + }); + + editor.getModels().forEach(onModelAdd); + } + + public dispose(): void { + this._disposables.forEach((d) => d && d.dispose()); + this._disposables = []; + } + + private _doValidate(resource: Uri, languageId: string): void { + this._worker(resource) + .then((worker) => { + return worker.doValidation(resource.toString()); + }) + .then((diagnostics) => { + const markers = diagnostics.map((d) => toDiagnostics(resource, d)); + let model = editor.getModel(resource); + if (model && model.getLanguageId() === languageId) { + editor.setModelMarkers(model, languageId, markers); + } + }) + .then(undefined, (err) => { + console.error(err); + }); + } +} + +function toSeverity(lsSeverity: number | undefined): MarkerSeverity { + switch (lsSeverity) { + case lsTypes.DiagnosticSeverity.Error: + return MarkerSeverity.Error; + case lsTypes.DiagnosticSeverity.Warning: + return MarkerSeverity.Warning; + case lsTypes.DiagnosticSeverity.Information: + return MarkerSeverity.Info; + case lsTypes.DiagnosticSeverity.Hint: + return MarkerSeverity.Hint; + default: + return MarkerSeverity.Info; + } +} + +function toDiagnostics(resource: Uri, diag: lsTypes.Diagnostic): editor.IMarkerData { + let code = typeof diag.code === 'number' ? String(diag.code) : diag.code; + + return { + severity: toSeverity(diag.severity), + startLineNumber: diag.range.start.line + 1, + startColumn: diag.range.start.character + 1, + endLineNumber: diag.range.end.line + 1, + endColumn: diag.range.end.character + 1, + message: diag.message, + code: code, + source: diag.source + }; +} + +//#endregion diff --git a/src/css/cssMode.ts b/src/css/cssMode.ts index ce475e8d..6e28ab08 100644 --- a/src/css/cssMode.ts +++ b/src/css/cssMode.ts @@ -92,7 +92,7 @@ export function setupMode(defaults: LanguageServiceDefaults): IDisposable { ); } if (modeConfiguration.diagnostics) { - providers.push(new languageFeatures.DiagnosticsAdapter(languageId, worker, defaults)); + providers.push(new languageFeatures.CSSDiagnosticsAdapter(languageId, worker, defaults)); } if (modeConfiguration.selectionRanges) { providers.push( diff --git a/src/css/languageFeatures.ts b/src/css/languageFeatures.ts index 0d943c00..d6dbe63a 100644 --- a/src/css/languageFeatures.ts +++ b/src/css/languageFeatures.ts @@ -14,10 +14,9 @@ import { Position, IRange, Range, - CancellationToken, - IDisposable, - MarkerSeverity + CancellationToken } from '../fillers/monaco-editor-core'; +import { DiagnosticsAdapter } from '../common/lspLanguageFeatures'; export interface WorkerAccessor { (first: Uri, ...more: Uri[]): Promise; @@ -25,124 +24,10 @@ export interface WorkerAccessor { // --- diagnostics --- --- -export class DiagnosticsAdapter { - private _disposables: IDisposable[] = []; - private _listener: { [uri: string]: IDisposable } = Object.create(null); - - constructor( - private _languageId: string, - private _worker: WorkerAccessor, - defaults: LanguageServiceDefaults - ) { - const onModelAdd = (model: editor.IModel): void => { - let modeId = model.getLanguageId(); - if (modeId !== this._languageId) { - return; - } - - let handle: number; - this._listener[model.uri.toString()] = model.onDidChangeContent(() => { - window.clearTimeout(handle); - handle = window.setTimeout(() => this._doValidate(model.uri, modeId), 500); - }); - - this._doValidate(model.uri, modeId); - }; - - const onModelRemoved = (model: editor.IModel): void => { - editor.setModelMarkers(model, this._languageId, []); - - let uriStr = model.uri.toString(); - let listener = this._listener[uriStr]; - if (listener) { - listener.dispose(); - delete this._listener[uriStr]; - } - }; - - this._disposables.push(editor.onDidCreateModel(onModelAdd)); - this._disposables.push(editor.onWillDisposeModel(onModelRemoved)); - this._disposables.push( - editor.onDidChangeModelLanguage((event) => { - onModelRemoved(event.model); - onModelAdd(event.model); - }) - ); - - this._disposables.push( - defaults.onDidChange((_) => { - editor.getModels().forEach((model) => { - if (model.getLanguageId() === this._languageId) { - onModelRemoved(model); - onModelAdd(model); - } - }); - }) - ); - - this._disposables.push({ - dispose: () => { - editor.getModels().forEach(onModelRemoved); - for (let key in this._listener) { - this._listener[key].dispose(); - } - } - }); - - editor.getModels().forEach(onModelAdd); +export class CSSDiagnosticsAdapter extends DiagnosticsAdapter { + constructor(languageId: string, worker: WorkerAccessor, defaults: LanguageServiceDefaults) { + super(languageId, worker, defaults.onDidChange); } - - public dispose(): void { - this._disposables.forEach((d) => d && d.dispose()); - this._disposables = []; - } - - private _doValidate(resource: Uri, languageId: string): void { - this._worker(resource) - .then((worker) => { - return worker.doValidation(resource.toString()); - }) - .then((diagnostics) => { - const markers = diagnostics.map((d) => toDiagnostics(resource, d)); - let model = editor.getModel(resource); - if (model && model.getLanguageId() === languageId) { - editor.setModelMarkers(model, languageId, markers); - } - }) - .then(undefined, (err) => { - console.error(err); - }); - } -} - -function toSeverity(lsSeverity: number | undefined): MarkerSeverity { - switch (lsSeverity) { - case lsTypes.DiagnosticSeverity.Error: - return MarkerSeverity.Error; - case lsTypes.DiagnosticSeverity.Warning: - return MarkerSeverity.Warning; - case lsTypes.DiagnosticSeverity.Information: - return MarkerSeverity.Info; - case lsTypes.DiagnosticSeverity.Hint: - return MarkerSeverity.Hint; - default: - return MarkerSeverity.Info; - } -} - -function toDiagnostics(resource: Uri, diag: lsTypes.Diagnostic): editor.IMarkerData { - let code = typeof diag.code === 'number' ? String(diag.code) : diag.code; - - return { - severity: toSeverity(diag.severity), - startLineNumber: diag.range.start.line + 1, - startColumn: diag.range.start.character + 1, - endLineNumber: diag.range.end.line + 1, - endColumn: diag.range.end.character + 1, - message: diag.message, - code: code, - source: diag.source - }; } // --- completion ------ diff --git a/src/json/jsonMode.ts b/src/json/jsonMode.ts index 395a864b..43b73d73 100644 --- a/src/json/jsonMode.ts +++ b/src/json/jsonMode.ts @@ -83,7 +83,7 @@ export function setupMode(defaults: LanguageServiceDefaults): IDisposable { ); } if (modeConfiguration.diagnostics) { - providers.push(new languageFeatures.DiagnosticsAdapter(languageId, worker, defaults)); + providers.push(new languageFeatures.JSONDiagnosticsAdapter(languageId, worker, defaults)); } if (modeConfiguration.selectionRanges) { providers.push( diff --git a/src/json/languageFeatures.ts b/src/json/languageFeatures.ts index f4937315..a633b554 100644 --- a/src/json/languageFeatures.ts +++ b/src/json/languageFeatures.ts @@ -14,10 +14,9 @@ import { Position, IRange, Range, - CancellationToken, - IDisposable, - MarkerSeverity + CancellationToken } from '../fillers/monaco-editor-core'; +import { DiagnosticsAdapter } from '../common/lspLanguageFeatures'; export interface WorkerAccessor { (...more: Uri[]): Promise; @@ -25,82 +24,20 @@ export interface WorkerAccessor { // --- diagnostics --- --- -export class DiagnosticsAdapter { - private _disposables: IDisposable[] = []; - private _listener: { [uri: string]: IDisposable } = Object.create(null); +export class JSONDiagnosticsAdapter extends DiagnosticsAdapter { + constructor(languageId: string, worker: WorkerAccessor, defaults: LanguageServiceDefaults) { + super(languageId, worker, defaults.onDidChange); - constructor( - private _languageId: string, - private _worker: WorkerAccessor, - defaults: LanguageServiceDefaults - ) { - const onModelAdd = (model: editor.IModel): void => { - let modeId = model.getLanguageId(); - if (modeId !== this._languageId) { - return; - } - - let handle: number; - this._listener[model.uri.toString()] = model.onDidChangeContent(() => { - window.clearTimeout(handle); - handle = window.setTimeout(() => this._doValidate(model.uri, modeId), 500); - }); - - this._doValidate(model.uri, modeId); - }; - - const onModelRemoved = (model: editor.IModel): void => { - editor.setModelMarkers(model, this._languageId, []); - - let uriStr = model.uri.toString(); - let listener = this._listener[uriStr]; - if (listener) { - listener.dispose(); - delete this._listener[uriStr]; - } - }; - - this._disposables.push(editor.onDidCreateModel(onModelAdd)); this._disposables.push( editor.onWillDisposeModel((model) => { - onModelRemoved(model); this._resetSchema(model.uri); }) ); this._disposables.push( editor.onDidChangeModelLanguage((event) => { - onModelRemoved(event.model); - onModelAdd(event.model); this._resetSchema(event.model.uri); }) ); - - this._disposables.push( - defaults.onDidChange((_) => { - editor.getModels().forEach((model) => { - if (model.getLanguageId() === this._languageId) { - onModelRemoved(model); - onModelAdd(model); - } - }); - }) - ); - - this._disposables.push({ - dispose: () => { - editor.getModels().forEach(onModelRemoved); - for (let key in this._listener) { - this._listener[key].dispose(); - } - } - }); - - editor.getModels().forEach(onModelAdd); - } - - public dispose(): void { - this._disposables.forEach((d) => d && d.dispose()); - this._disposables = []; } private _resetSchema(resource: Uri): void { @@ -108,53 +45,6 @@ export class DiagnosticsAdapter { worker.resetSchema(resource.toString()); }); } - - private _doValidate(resource: Uri, languageId: string): void { - this._worker(resource) - .then((worker) => { - return worker.doValidation(resource.toString()); - }) - .then((diagnostics) => { - const markers = diagnostics.map((d) => toDiagnostics(resource, d)); - let model = editor.getModel(resource); - if (model && model.getLanguageId() === languageId) { - editor.setModelMarkers(model, languageId, markers); - } - }) - .then(undefined, (err) => { - console.error(err); - }); - } -} - -function toSeverity(lsSeverity: number | undefined): MarkerSeverity { - switch (lsSeverity) { - case lsTypes.DiagnosticSeverity.Error: - return MarkerSeverity.Error; - case lsTypes.DiagnosticSeverity.Warning: - return MarkerSeverity.Warning; - case lsTypes.DiagnosticSeverity.Information: - return MarkerSeverity.Info; - case lsTypes.DiagnosticSeverity.Hint: - return MarkerSeverity.Hint; - default: - return MarkerSeverity.Info; - } -} - -function toDiagnostics(resource: Uri, diag: lsTypes.Diagnostic): editor.IMarkerData { - let code = typeof diag.code === 'number' ? String(diag.code) : diag.code; - - return { - severity: toSeverity(diag.severity), - startLineNumber: diag.range.start.line + 1, - startColumn: diag.range.start.character + 1, - endLineNumber: diag.range.end.line + 1, - endColumn: diag.range.end.character + 1, - message: diag.message, - code: code, - source: diag.source - }; } // --- completion ------