/*--------------------------------------------------------------------------------------------- * 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