From 8fa3ca42520e00b4fe6cb2534f1e8a3d413447b2 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Wed, 18 Aug 2021 21:40:44 +0200 Subject: [PATCH] Convert classes to factory functions TypeScript is better at inferring types when using simple functions and objects instead of classes. As a result many type annotations have now been removed without impacting type safety. Some other benefits are this can be minified better and private fields are now truly private variables. --- index.d.ts | 1 + src/languageFeatures.ts | 354 +++++++++++++++++++------------------ src/monaco.contribution.ts | 47 ++--- src/workerManager.ts | 113 ++++++------ src/yaml.worker.ts | 7 +- src/yamlMode.ts | 34 ++-- src/yamlWorker.ts | 144 ++++++++------- 7 files changed, 355 insertions(+), 345 deletions(-) diff --git a/index.d.ts b/index.d.ts index 6415cd6..9f922ed 100644 --- a/index.d.ts +++ b/index.d.ts @@ -57,6 +57,7 @@ declare module 'monaco-editor/esm/vs/editor/editor.api' { export interface LanguageServiceDefaults { readonly onDidChange: IEvent; + readonly languageId: string; readonly diagnosticsOptions: DiagnosticsOptions; setDiagnosticsOptions: (options: DiagnosticsOptions) => void; } diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index ef34fb8..9e3db92 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -11,7 +11,6 @@ import { import * as ls from 'vscode-languageserver-types'; import { CustomFormatterOptions } from 'yaml-language-server/lib/esm/languageservice/yamlLanguageService'; -import { LanguageServiceDefaultsImpl } from './monaco.contribution'; import { YAMLWorker } from './yamlWorker'; export type WorkerAccessor = (...more: Uri[]) => PromiseLike; @@ -48,85 +47,22 @@ function toDiagnostics(resource: Uri, diag: ls.Diagnostic): editor.IMarkerData { }; } -export class DiagnosticsAdapter { - private _disposables: IDisposable[] = []; - private _listener: Record = Object.create(null); +export function createDiagnosticsAdapter( + languageId: string, + getWorker: WorkerAccessor, + defaults: languages.yaml.LanguageServiceDefaults, +): IDisposable { + let disposables: IDisposable[] = []; + const listeners: Record = Object.create(null); - constructor( - private _languageId: string, - private _worker: WorkerAccessor, - defaults: LanguageServiceDefaultsImpl, - ) { - const onModelAdd = (model: editor.IModel): void => { - const modeId = model.getModeId(); - if (modeId !== this._languageId) { - return; - } - - let handle: number; - this._listener[String(toString)] = model.onDidChangeContent(() => { - clearTimeout(handle); - handle = setTimeout(() => this._doValidate(model.uri, modeId), 500); - }); - - this._doValidate(model.uri, modeId); - }; - - const onModelRemoved = (model: editor.IModel): void => { - editor.setModelMarkers(model, this._languageId, []); - const uriStr = String(model.uri); - const listener = this._listener[uriStr]; - if (listener) { - listener.dispose(); - delete this._listener[uriStr]; - } - }; - - this._disposables.push( - editor.onDidCreateModel(onModelAdd), - editor.onWillDisposeModel((model) => { - onModelRemoved(model); - this._resetSchema(model.uri); - }), - editor.onDidChangeModelLanguage((event) => { - onModelRemoved(event.model); - onModelAdd(event.model); - this._resetSchema(event.model.uri); - }), - defaults.onDidChange(() => { - editor.getModels().forEach((model) => { - if (model.getModeId() === this._languageId) { - onModelRemoved(model); - onModelAdd(model); - } - }); - }), - { - dispose: () => { - editor.getModels().forEach(onModelRemoved); - for (const disposable of Object.values(this._listener)) { - disposable.dispose(); - } - }, - }, - ); - - editor.getModels().forEach(onModelAdd); - } - - dispose(): void { - this._disposables.forEach((d) => d && d.dispose()); - this._disposables = []; - } - - private _resetSchema(resource: Uri): void { - this._worker().then((worker) => { + const resetSchema = (resource: Uri): void => { + getWorker().then((worker) => { worker.resetSchema(String(resource)); }); - } + }; - private _doValidate(resource: Uri, languageId: string): void { - this._worker(resource) + const doValidate = (resource: Uri, languageId: string): void => { + getWorker(resource) .then((worker) => worker.doValidation(String(resource)).then((diagnostics) => { const markers = diagnostics.map((d) => toDiagnostics(resource, d)); @@ -139,7 +75,70 @@ export class DiagnosticsAdapter { .then(undefined, (err) => { console.error(err); }); - } + }; + + const onModelAdd = (model: editor.IModel): void => { + const modeId = model.getModeId(); + if (modeId !== languageId) { + return; + } + + let handle: number; + listeners[String(toString)] = model.onDidChangeContent(() => { + clearTimeout(handle); + handle = setTimeout(() => doValidate(model.uri, modeId), 500); + }); + + doValidate(model.uri, modeId); + }; + + const onModelRemoved = (model: editor.IModel): void => { + editor.setModelMarkers(model, languageId, []); + const uriStr = String(model.uri); + const listener = listeners[uriStr]; + if (listener) { + listener.dispose(); + delete listeners[uriStr]; + } + }; + + disposables.push( + editor.onDidCreateModel(onModelAdd), + editor.onWillDisposeModel((model) => { + onModelRemoved(model); + resetSchema(model.uri); + }), + editor.onDidChangeModelLanguage((event) => { + onModelRemoved(event.model); + onModelAdd(event.model); + resetSchema(event.model.uri); + }), + defaults.onDidChange(() => { + editor.getModels().forEach((model) => { + if (model.getModeId() === languageId) { + onModelRemoved(model); + onModelAdd(model); + } + }); + }), + { + dispose: () => { + editor.getModels().forEach(onModelRemoved); + for (const disposable of Object.values(listeners)) { + disposable.dispose(); + } + }, + }, + ); + + editor.getModels().forEach(onModelAdd); + + return { + dispose() { + disposables.forEach((d) => d && d.dispose()); + disposables = []; + }, + }; } // --- completion ------ @@ -218,64 +217,66 @@ function toTextEdit(textEdit: ls.TextEdit): editor.ISingleEditOperation { }; } -export class CompletionAdapter implements languages.CompletionItemProvider { - triggetCharacters = [' ', ':']; +export function createCompletionItemProvider( + getWorker: WorkerAccessor, +): languages.CompletionItemProvider { + return { + triggerCharacters: [' ', ':'], - constructor(private _worker: WorkerAccessor) {} + provideCompletionItems( + model: editor.IReadOnlyModel, + position: Position, + ): PromiseLike { + const resource = model.uri; - provideCompletionItems( - model: editor.IReadOnlyModel, - position: Position, - ): PromiseLike { - const resource = model.uri; + return getWorker(resource) + .then((worker) => worker.doComplete(String(resource), fromPosition(position))) + .then((info) => { + if (!info) { + return; + } - return this._worker(resource) - .then((worker) => worker.doComplete(String(resource), fromPosition(position))) - .then((info) => { - if (!info) { - return; - } + const wordInfo = model.getWordUntilPosition(position); + const wordRange = new Range( + position.lineNumber, + wordInfo.startColumn, + position.lineNumber, + wordInfo.endColumn, + ); - const wordInfo = model.getWordUntilPosition(position); - const wordRange = new Range( - position.lineNumber, - wordInfo.startColumn, - position.lineNumber, - wordInfo.endColumn, - ); + const items = info.items.map((entry) => { + const item: languages.CompletionItem = { + label: entry.label, + insertText: entry.insertText || entry.label, + sortText: entry.sortText, + filterText: entry.filterText, + documentation: entry.documentation, + detail: entry.detail, + kind: toCompletionItemKind(entry.kind), + range: wordRange, + }; + if (entry.textEdit) { + item.range = toRange( + 'range' in entry.textEdit ? entry.textEdit.range : entry.textEdit.replace, + ); + item.insertText = entry.textEdit.newText; + } + if (entry.additionalTextEdits) { + item.additionalTextEdits = entry.additionalTextEdits.map(toTextEdit); + } + if (entry.insertTextFormat === ls.InsertTextFormat.Snippet) { + item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet; + } + return item; + }); - const items: languages.CompletionItem[] = info.items.map((entry) => { - const item: languages.CompletionItem = { - label: entry.label, - insertText: entry.insertText || entry.label, - sortText: entry.sortText, - filterText: entry.filterText, - documentation: entry.documentation, - detail: entry.detail, - kind: toCompletionItemKind(entry.kind), - range: wordRange, + return { + isIncomplete: info.isIncomplete, + suggestions: items, }; - if (entry.textEdit) { - item.range = toRange( - 'range' in entry.textEdit ? entry.textEdit.range : entry.textEdit.replace, - ); - item.insertText = entry.textEdit.newText; - } - if (entry.additionalTextEdits) { - item.additionalTextEdits = entry.additionalTextEdits.map(toTextEdit); - } - if (entry.insertTextFormat === ls.InsertTextFormat.Snippet) { - item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet; - } - return item; }); - - return { - isIncomplete: info.isIncomplete, - suggestions: items, - }; - }); - } + }, + }; } function isMarkupContent(thing: unknown): thing is ls.MarkupContent { @@ -316,24 +317,24 @@ function toMarkedStringArray( // --- hover ------ -export class HoverAdapter implements languages.HoverProvider { - constructor(private _worker: WorkerAccessor) {} +export function createHoverProvider(getWorker: WorkerAccessor): languages.HoverProvider { + return { + provideHover(model, position) { + const resource = model.uri; - provideHover(model: editor.IReadOnlyModel, position: Position): PromiseLike { - const resource = model.uri; - - return this._worker(resource) - .then((worker) => worker.doHover(String(resource), fromPosition(position))) - .then((info) => { - if (!info) { - return; - } - return { - range: toRange(info.range), - contents: toMarkedStringArray(info.contents), - } as languages.Hover; - }); - } + return getWorker(resource) + .then((worker) => worker.doHover(String(resource), fromPosition(position))) + .then((info) => { + if (!info) { + return; + } + return { + range: toRange(info.range), + contents: toMarkedStringArray(info.contents), + } as languages.Hover; + }); + }, + }; } // --- document symbols ------ @@ -395,21 +396,23 @@ function toDocumentSymbol(item: ls.DocumentSymbol): languages.DocumentSymbol { }; } -export class DocumentSymbolAdapter implements languages.DocumentSymbolProvider { - constructor(private _worker: WorkerAccessor) {} +export function createDocumentSymbolProvider( + getWorker: WorkerAccessor, +): languages.DocumentSymbolProvider { + return { + provideDocumentSymbols(model) { + const resource = model.uri; - provideDocumentSymbols(model: editor.IReadOnlyModel): PromiseLike { - const resource = model.uri; - - return this._worker(resource) - .then((worker) => worker.findDocumentSymbols(String(resource))) - .then((items) => { - if (!items) { - return; - } - return items.map((item) => toDocumentSymbol(item)); - }); - } + return getWorker(resource) + .then((worker) => worker.findDocumentSymbols(String(resource))) + .then((items) => { + if (!items) { + return; + } + return items.map((item) => toDocumentSymbol(item)); + }); + }, + }; } function fromFormattingOptions( @@ -422,22 +425,21 @@ function fromFormattingOptions( }; } -export class DocumentFormattingEditProvider implements languages.DocumentFormattingEditProvider { - constructor(private _worker: WorkerAccessor) {} +export function createDocumentFormattingEditProvider( + getWorker: WorkerAccessor, +): languages.DocumentFormattingEditProvider { + return { + provideDocumentFormattingEdits(model, options) { + const resource = model.uri; - provideDocumentFormattingEdits( - model: editor.IReadOnlyModel, - options: languages.FormattingOptions, - ): PromiseLike { - const resource = model.uri; - - return this._worker(resource).then((worker) => - worker.format(String(resource), fromFormattingOptions(options)).then((edits) => { - if (!edits || edits.length === 0) { - return; - } - return edits.map(toTextEdit); - }), - ); - } + return getWorker(resource).then((worker) => + worker.format(String(resource), fromFormattingOptions(options)).then((edits) => { + if (!edits || edits.length === 0) { + return; + } + return edits.map(toTextEdit); + }), + ); + }, + }; } diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index ce7012c..be91e1b 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -1,35 +1,36 @@ -import { Emitter, IEvent, languages } from 'monaco-editor/esm/vs/editor/editor.api'; +import { Emitter, languages } from 'monaco-editor/esm/vs/editor/editor.api'; import { setupMode } from './yamlMode'; // --- YAML configuration and defaults --------- -export class LanguageServiceDefaultsImpl implements languages.yaml.LanguageServiceDefaults { - private _onDidChange = new Emitter(); - private _diagnosticsOptions: languages.yaml.DiagnosticsOptions; - private _languageId: string; +export function createLanguageServiceDefaults( + languageId: string, + initialDiagnosticsOptions: languages.yaml.DiagnosticsOptions, +): languages.yaml.LanguageServiceDefaults { + const onDidChange = new Emitter(); + let diagnosticsOptions = initialDiagnosticsOptions; - constructor(languageId: string, diagnosticsOptions: languages.yaml.DiagnosticsOptions) { - this._languageId = languageId; - this.setDiagnosticsOptions(diagnosticsOptions); - } + const languageServiceDefaults: languages.yaml.LanguageServiceDefaults = { + get onDidChange() { + return onDidChange.event; + }, - get onDidChange(): IEvent { - return this._onDidChange.event; - } + get languageId() { + return languageId; + }, - get languageId(): string { - return this._languageId; - } + get diagnosticsOptions() { + return diagnosticsOptions; + }, - get diagnosticsOptions(): languages.yaml.DiagnosticsOptions { - return this._diagnosticsOptions; - } + setDiagnosticsOptions(options) { + diagnosticsOptions = options || {}; + onDidChange.fire(languageServiceDefaults); + }, + }; - setDiagnosticsOptions(options: languages.yaml.DiagnosticsOptions): void { - this._diagnosticsOptions = options || Object.create(null); - this._onDidChange.fire(this); - } + return languageServiceDefaults; } const diagnosticDefault: languages.yaml.DiagnosticsOptions = { @@ -38,7 +39,7 @@ const diagnosticDefault: languages.yaml.DiagnosticsOptions = { enableSchemaRequest: false, }; -const yamlDefaults = new LanguageServiceDefaultsImpl('yaml', diagnosticDefault); +const yamlDefaults = createLanguageServiceDefaults('yaml', diagnosticDefault); // Export API function createAPI(): typeof languages.yaml { diff --git a/src/workerManager.ts b/src/workerManager.ts index 5504fe0..6e50983 100644 --- a/src/workerManager.ts +++ b/src/workerManager.ts @@ -1,85 +1,82 @@ -import { editor, IDisposable, Uri } from 'monaco-editor/esm/vs/editor/editor.api'; +import { editor, IDisposable, languages, Uri } from 'monaco-editor/esm/vs/editor/editor.api'; -import { LanguageServiceDefaultsImpl } from './monaco.contribution'; import { YAMLWorker } from './yamlWorker'; +export interface WorkerManager extends IDisposable { + getLanguageServiceWorker: (...resources: Uri[]) => Promise; +} + // 2min const STOP_WHEN_IDLE_FOR = 2 * 60 * 1000; -export class WorkerManager { - private _defaults: LanguageServiceDefaultsImpl; - private _idleCheckInterval: number; - private _lastUsedTime: number; - private _configChangeListener: IDisposable; +export function createWorkerManager( + defaults: languages.yaml.LanguageServiceDefaults, +): WorkerManager { + let worker: editor.MonacoWebWorker; + let client: Promise; + let lastUsedTime = 0; - private _worker: editor.MonacoWebWorker; - private _client: Promise; - - constructor(defaults: LanguageServiceDefaultsImpl) { - this._defaults = defaults; - this._worker = null; - this._idleCheckInterval = setInterval(() => this._checkIfIdle(), 30 * 1000); - this._lastUsedTime = 0; - this._configChangeListener = this._defaults.onDidChange(() => this._stopWorker()); - } - - dispose(): void { - clearInterval(this._idleCheckInterval); - this._configChangeListener.dispose(); - this._stopWorker(); - } - - getLanguageServiceWorker(...resources: Uri[]): Promise { - let _client: YAMLWorker; - return this._getClient() - .then((client) => { - _client = client; - }) - .then(() => this._worker.withSyncedResources(resources)) - .then(() => _client); - } - - private _stopWorker(): void { - if (this._worker) { - this._worker.dispose(); - this._worker = null; + const stopWorker = (): void => { + if (worker) { + worker.dispose(); + worker = null; } - this._client = null; - } + client = null; + }; - private _checkIfIdle(): void { - if (!this._worker) { + const idleCheckInterval = setInterval(() => { + if (!worker) { return; } - const timePassedSinceLastUsed = Date.now() - this._lastUsedTime; + const timePassedSinceLastUsed = Date.now() - lastUsedTime; if (timePassedSinceLastUsed > STOP_WHEN_IDLE_FOR) { - this._stopWorker(); + stopWorker(); } - } + }, 30 * 1000); - private _getClient(): Promise { - this._lastUsedTime = Date.now(); + const configChangeListener = defaults.onDidChange(() => stopWorker()); - if (!this._client) { - this._worker = editor.createWebWorker({ + const getClient = (): Promise => { + lastUsedTime = Date.now(); + + if (!client) { + worker = editor.createWebWorker({ // Module that exports the create() method and returns a `YAMLWorker` instance moduleId: 'vs/language/yaml/yamlWorker', - label: this._defaults.languageId, + label: defaults.languageId, // Passed in to the create() method createData: { - languageSettings: this._defaults.diagnosticsOptions, - languageId: this._defaults.languageId, - enableSchemaRequest: this._defaults.diagnosticsOptions.enableSchemaRequest, - prefix: this._defaults.diagnosticsOptions.prefix, - isKubernetes: this._defaults.diagnosticsOptions.isKubernetes, + languageSettings: defaults.diagnosticsOptions, + languageId: defaults.languageId, + enableSchemaRequest: defaults.diagnosticsOptions.enableSchemaRequest, + prefix: defaults.diagnosticsOptions.prefix, + isKubernetes: defaults.diagnosticsOptions.isKubernetes, }, }); - this._client = this._worker.getProxy(); + client = worker.getProxy(); } - return this._client; - } + return client; + }; + + return { + dispose() { + clearInterval(idleCheckInterval); + configChangeListener.dispose(); + stopWorker(); + }, + + getLanguageServiceWorker(...resources) { + let _client: YAMLWorker; + return getClient() + .then((client) => { + _client = client; + }) + .then(() => worker.withSyncedResources(resources)) + .then(() => _client); + }, + }; } diff --git a/src/yaml.worker.ts b/src/yaml.worker.ts index 173da3e..2c17ae4 100644 --- a/src/yaml.worker.ts +++ b/src/yaml.worker.ts @@ -1,8 +1,7 @@ -import * as worker from 'monaco-editor/esm/vs/editor/editor.worker'; +import { initialize } from 'monaco-editor/esm/vs/editor/editor.worker'; -import { YAMLWorker } from './yamlWorker'; +import { createYAMLWorker } from './yamlWorker'; self.onmessage = () => { - // Ignore the first message - worker.initialize((ctx, createData) => new YAMLWorker(ctx, createData)); + initialize((ctx, createData) => Object.create(createYAMLWorker(ctx, createData))); }; diff --git a/src/yamlMode.ts b/src/yamlMode.ts index 638cd2a..5509e7e 100644 --- a/src/yamlMode.ts +++ b/src/yamlMode.ts @@ -1,8 +1,14 @@ import { IDisposable, languages, Uri } from 'monaco-editor/esm/vs/editor/editor.api'; -import * as languageFeatures from './languageFeatures'; -import { LanguageServiceDefaultsImpl } from './monaco.contribution'; -import { WorkerManager } from './workerManager'; +import { + createCompletionItemProvider, + createDiagnosticsAdapter, + createDocumentFormattingEditProvider, + createDocumentSymbolProvider, + createHoverProvider, + WorkerAccessor, +} from './languageFeatures'; +import { createWorkerManager } from './workerManager'; import { YAMLWorker } from './yamlWorker'; const richEditConfiguration: languages.LanguageConfiguration = { @@ -37,32 +43,26 @@ const richEditConfiguration: languages.LanguageConfiguration = { ], }; -export function setupMode(defaults: LanguageServiceDefaultsImpl): void { +export function setupMode(defaults: languages.yaml.LanguageServiceDefaults): void { const disposables: IDisposable[] = []; - const client = new WorkerManager(defaults); + const client = createWorkerManager(defaults); disposables.push(client); - const worker: languageFeatures.WorkerAccessor = (...uris: Uri[]): Promise => + const worker: WorkerAccessor = (...uris: Uri[]): Promise => client.getLanguageServiceWorker(...uris); const { languageId } = defaults; disposables.push( - languages.registerCompletionItemProvider( - languageId, - new languageFeatures.CompletionAdapter(worker), - ), - languages.registerHoverProvider(languageId, new languageFeatures.HoverAdapter(worker)), - languages.registerDocumentSymbolProvider( - languageId, - new languageFeatures.DocumentSymbolAdapter(worker), - ), + languages.registerCompletionItemProvider(languageId, createCompletionItemProvider(worker)), + languages.registerHoverProvider(languageId, createHoverProvider(worker)), + languages.registerDocumentSymbolProvider(languageId, createDocumentSymbolProvider(worker)), languages.registerDocumentFormattingEditProvider( languageId, - new languageFeatures.DocumentFormattingEditProvider(worker), + createDocumentFormattingEditProvider(worker), ), - new languageFeatures.DiagnosticsAdapter(languageId, worker, defaults), + createDiagnosticsAdapter(languageId, worker, defaults), languages.setLanguageConfiguration(languageId, richEditConfiguration), ); } diff --git a/src/yamlWorker.ts b/src/yamlWorker.ts index 32c07e6..2d05b25 100644 --- a/src/yamlWorker.ts +++ b/src/yamlWorker.ts @@ -1,6 +1,10 @@ import { worker } from 'monaco-editor/esm/vs/editor/editor.api'; import * as ls from 'vscode-languageserver-types'; -import * as yamlService from 'yaml-language-server/lib/esm/languageservice/yamlLanguageService'; +import { + CustomFormatterOptions, + getLanguageService, + LanguageSettings, +} from 'yaml-language-server/lib/esm/languageservice/yamlLanguageService'; let defaultSchemaRequestService: (url: string) => PromiseLike; @@ -8,90 +12,96 @@ if (typeof fetch !== 'undefined') { defaultSchemaRequestService = (url) => fetch(url).then((response) => response.text()); } -export class YAMLWorker { - private _ctx: worker.IWorkerContext; - private _languageService: yamlService.LanguageService; - private _languageSettings: yamlService.LanguageSettings; - private _languageId: string; - private _isKubernetes: boolean; +export interface YAMLWorker { + doValidation: (uri: string) => PromiseLike; - constructor(ctx: worker.IWorkerContext, createData: ICreateData) { - const prefix = createData.prefix || ''; - const service = (url: string): PromiseLike => - defaultSchemaRequestService(`${prefix}${url}`); - this._ctx = ctx; - this._languageSettings = createData.languageSettings; - this._languageId = createData.languageId; - this._languageService = yamlService.getLanguageService( - createData.enableSchemaRequest && service, - null, - [], - ); - this._isKubernetes = createData.isKubernetes || false; - this._languageService.configure({ - ...this._languageSettings, - hover: true, - isKubernetes: this._isKubernetes, - }); - } + doComplete: (uri: string, position: ls.Position) => PromiseLike; - doValidation(uri: string): PromiseLike { - const document = this._getTextDocument(uri); - if (document) { - return this._languageService.doValidation(document, this._isKubernetes); - } - return Promise.resolve([]); - } + doResolve: (item: ls.CompletionItem) => PromiseLike; - doComplete(uri: string, position: ls.Position): PromiseLike { - const document = this._getTextDocument(uri); - return this._languageService.doComplete(document, position, this._isKubernetes); - } + doHover: (uri: string, position: ls.Position) => PromiseLike; - doResolve(item: ls.CompletionItem): PromiseLike { - return this._languageService.doResolve(item); - } + format: (uri: string, options: CustomFormatterOptions) => PromiseLike; - doHover(uri: string, position: ls.Position): PromiseLike { - const document = this._getTextDocument(uri); - return this._languageService.doHover(document, position); - } + resetSchema: (uri: string) => PromiseLike; - format(uri: string, options: yamlService.CustomFormatterOptions): PromiseLike { - const document = this._getTextDocument(uri); - const textEdits = this._languageService.doFormat(document, options); - return Promise.resolve(textEdits); - } + findDocumentSymbols: (uri: string) => PromiseLike; +} - resetSchema(uri: string): PromiseLike { - return Promise.resolve(this._languageService.resetSchema(uri)); - } +export function createYAMLWorker( + ctx: worker.IWorkerContext, + { + enableSchemaRequest, + isKubernetes = false, + languageId, + languageSettings, + prefix = '', + }: ICreateData, +): YAMLWorker { + const service = (url: string): PromiseLike => + defaultSchemaRequestService(`${prefix}${url}`); + const languageService = getLanguageService(enableSchemaRequest && service, null, []); + languageService.configure({ + ...languageSettings, + hover: true, + isKubernetes, + }); - findDocumentSymbols(uri: string): PromiseLike { - const document = this._getTextDocument(uri); - const symbols = this._languageService.findDocumentSymbols2(document); - return Promise.resolve(symbols); - } - - private _getTextDocument(uri: string): ls.TextDocument { - const models = this._ctx.getMirrorModels(); + const getTextDocument = (uri: string): ls.TextDocument => { + const models = ctx.getMirrorModels(); for (const model of models) { if (String(model.uri) === uri) { - return ls.TextDocument.create(uri, this._languageId, model.version, model.getValue()); + return ls.TextDocument.create(uri, languageId, model.version, model.getValue()); } } return null; - } + }; + + return { + doValidation(uri) { + const document = getTextDocument(uri); + if (document) { + return languageService.doValidation(document, isKubernetes); + } + return Promise.resolve([]); + }, + + doComplete(uri, position) { + const document = getTextDocument(uri); + return languageService.doComplete(document, position, isKubernetes); + }, + + doResolve(item) { + return languageService.doResolve(item); + }, + + doHover(uri, position) { + const document = getTextDocument(uri); + return languageService.doHover(document, position); + }, + + format(uri, options) { + const document = getTextDocument(uri); + const textEdits = languageService.doFormat(document, options); + return Promise.resolve(textEdits); + }, + + resetSchema(uri) { + return Promise.resolve(languageService.resetSchema(uri)); + }, + + findDocumentSymbols(uri) { + const document = getTextDocument(uri); + const symbols = languageService.findDocumentSymbols2(document); + return Promise.resolve(symbols); + }, + }; } export interface ICreateData { languageId: string; - languageSettings: yamlService.LanguageSettings; + languageSettings: LanguageSettings; enableSchemaRequest: boolean; prefix?: string; isKubernetes?: boolean; } - -export function create(ctx: worker.IWorkerContext, createData: ICreateData): YAMLWorker { - return new YAMLWorker(ctx, createData); -}