mirror of
https://github.com/danbulant/monaco-yaml
synced 2026-05-24 12:21:53 +00:00
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.
This commit is contained in:
parent
bdfa1ef4e7
commit
8fa3ca4252
7 changed files with 355 additions and 345 deletions
1
index.d.ts
vendored
1
index.d.ts
vendored
|
|
@ -57,6 +57,7 @@ declare module 'monaco-editor/esm/vs/editor/editor.api' {
|
||||||
|
|
||||||
export interface LanguageServiceDefaults {
|
export interface LanguageServiceDefaults {
|
||||||
readonly onDidChange: IEvent<LanguageServiceDefaults>;
|
readonly onDidChange: IEvent<LanguageServiceDefaults>;
|
||||||
|
readonly languageId: string;
|
||||||
readonly diagnosticsOptions: DiagnosticsOptions;
|
readonly diagnosticsOptions: DiagnosticsOptions;
|
||||||
setDiagnosticsOptions: (options: DiagnosticsOptions) => void;
|
setDiagnosticsOptions: (options: DiagnosticsOptions) => void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import {
|
||||||
import * as ls from 'vscode-languageserver-types';
|
import * as ls from 'vscode-languageserver-types';
|
||||||
import { CustomFormatterOptions } from 'yaml-language-server/lib/esm/languageservice/yamlLanguageService';
|
import { CustomFormatterOptions } from 'yaml-language-server/lib/esm/languageservice/yamlLanguageService';
|
||||||
|
|
||||||
import { LanguageServiceDefaultsImpl } from './monaco.contribution';
|
|
||||||
import { YAMLWorker } from './yamlWorker';
|
import { YAMLWorker } from './yamlWorker';
|
||||||
|
|
||||||
export type WorkerAccessor = (...more: Uri[]) => PromiseLike<YAMLWorker>;
|
export type WorkerAccessor = (...more: Uri[]) => PromiseLike<YAMLWorker>;
|
||||||
|
|
@ -48,85 +47,22 @@ function toDiagnostics(resource: Uri, diag: ls.Diagnostic): editor.IMarkerData {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DiagnosticsAdapter {
|
export function createDiagnosticsAdapter(
|
||||||
private _disposables: IDisposable[] = [];
|
languageId: string,
|
||||||
private _listener: Record<string, IDisposable> = Object.create(null);
|
getWorker: WorkerAccessor,
|
||||||
|
defaults: languages.yaml.LanguageServiceDefaults,
|
||||||
|
): IDisposable {
|
||||||
|
let disposables: IDisposable[] = [];
|
||||||
|
const listeners: Record<string, IDisposable> = Object.create(null);
|
||||||
|
|
||||||
constructor(
|
const resetSchema = (resource: Uri): void => {
|
||||||
private _languageId: string,
|
getWorker().then((worker) => {
|
||||||
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) => {
|
|
||||||
worker.resetSchema(String(resource));
|
worker.resetSchema(String(resource));
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
private _doValidate(resource: Uri, languageId: string): void {
|
const doValidate = (resource: Uri, languageId: string): void => {
|
||||||
this._worker(resource)
|
getWorker(resource)
|
||||||
.then((worker) =>
|
.then((worker) =>
|
||||||
worker.doValidation(String(resource)).then((diagnostics) => {
|
worker.doValidation(String(resource)).then((diagnostics) => {
|
||||||
const markers = diagnostics.map((d) => toDiagnostics(resource, d));
|
const markers = diagnostics.map((d) => toDiagnostics(resource, d));
|
||||||
|
|
@ -139,7 +75,70 @@ export class DiagnosticsAdapter {
|
||||||
.then(undefined, (err) => {
|
.then(undefined, (err) => {
|
||||||
console.error(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 ------
|
// --- completion ------
|
||||||
|
|
@ -218,64 +217,66 @@ function toTextEdit(textEdit: ls.TextEdit): editor.ISingleEditOperation {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompletionAdapter implements languages.CompletionItemProvider {
|
export function createCompletionItemProvider(
|
||||||
triggetCharacters = [' ', ':'];
|
getWorker: WorkerAccessor,
|
||||||
|
): languages.CompletionItemProvider {
|
||||||
|
return {
|
||||||
|
triggerCharacters: [' ', ':'],
|
||||||
|
|
||||||
constructor(private _worker: WorkerAccessor) {}
|
provideCompletionItems(
|
||||||
|
model: editor.IReadOnlyModel,
|
||||||
|
position: Position,
|
||||||
|
): PromiseLike<languages.CompletionList> {
|
||||||
|
const resource = model.uri;
|
||||||
|
|
||||||
provideCompletionItems(
|
return getWorker(resource)
|
||||||
model: editor.IReadOnlyModel,
|
.then((worker) => worker.doComplete(String(resource), fromPosition(position)))
|
||||||
position: Position,
|
.then((info) => {
|
||||||
): PromiseLike<languages.CompletionList> {
|
if (!info) {
|
||||||
const resource = model.uri;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return this._worker(resource)
|
const wordInfo = model.getWordUntilPosition(position);
|
||||||
.then((worker) => worker.doComplete(String(resource), fromPosition(position)))
|
const wordRange = new Range(
|
||||||
.then((info) => {
|
position.lineNumber,
|
||||||
if (!info) {
|
wordInfo.startColumn,
|
||||||
return;
|
position.lineNumber,
|
||||||
}
|
wordInfo.endColumn,
|
||||||
|
);
|
||||||
|
|
||||||
const wordInfo = model.getWordUntilPosition(position);
|
const items = info.items.map((entry) => {
|
||||||
const wordRange = new Range(
|
const item: languages.CompletionItem = {
|
||||||
position.lineNumber,
|
label: entry.label,
|
||||||
wordInfo.startColumn,
|
insertText: entry.insertText || entry.label,
|
||||||
position.lineNumber,
|
sortText: entry.sortText,
|
||||||
wordInfo.endColumn,
|
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) => {
|
return {
|
||||||
const item: languages.CompletionItem = {
|
isIncomplete: info.isIncomplete,
|
||||||
label: entry.label,
|
suggestions: items,
|
||||||
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;
|
|
||||||
});
|
});
|
||||||
|
},
|
||||||
return {
|
};
|
||||||
isIncomplete: info.isIncomplete,
|
|
||||||
suggestions: items,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMarkupContent(thing: unknown): thing is ls.MarkupContent {
|
function isMarkupContent(thing: unknown): thing is ls.MarkupContent {
|
||||||
|
|
@ -316,24 +317,24 @@ function toMarkedStringArray(
|
||||||
|
|
||||||
// --- hover ------
|
// --- hover ------
|
||||||
|
|
||||||
export class HoverAdapter implements languages.HoverProvider {
|
export function createHoverProvider(getWorker: WorkerAccessor): languages.HoverProvider {
|
||||||
constructor(private _worker: WorkerAccessor) {}
|
return {
|
||||||
|
provideHover(model, position) {
|
||||||
|
const resource = model.uri;
|
||||||
|
|
||||||
provideHover(model: editor.IReadOnlyModel, position: Position): PromiseLike<languages.Hover> {
|
return getWorker(resource)
|
||||||
const resource = model.uri;
|
.then((worker) => worker.doHover(String(resource), fromPosition(position)))
|
||||||
|
.then((info) => {
|
||||||
return this._worker(resource)
|
if (!info) {
|
||||||
.then((worker) => worker.doHover(String(resource), fromPosition(position)))
|
return;
|
||||||
.then((info) => {
|
}
|
||||||
if (!info) {
|
return {
|
||||||
return;
|
range: toRange(info.range),
|
||||||
}
|
contents: toMarkedStringArray(info.contents),
|
||||||
return {
|
} as languages.Hover;
|
||||||
range: toRange(info.range),
|
});
|
||||||
contents: toMarkedStringArray(info.contents),
|
},
|
||||||
} as languages.Hover;
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- document symbols ------
|
// --- document symbols ------
|
||||||
|
|
@ -395,21 +396,23 @@ function toDocumentSymbol(item: ls.DocumentSymbol): languages.DocumentSymbol {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DocumentSymbolAdapter implements languages.DocumentSymbolProvider {
|
export function createDocumentSymbolProvider(
|
||||||
constructor(private _worker: WorkerAccessor) {}
|
getWorker: WorkerAccessor,
|
||||||
|
): languages.DocumentSymbolProvider {
|
||||||
|
return {
|
||||||
|
provideDocumentSymbols(model) {
|
||||||
|
const resource = model.uri;
|
||||||
|
|
||||||
provideDocumentSymbols(model: editor.IReadOnlyModel): PromiseLike<languages.DocumentSymbol[]> {
|
return getWorker(resource)
|
||||||
const resource = model.uri;
|
.then((worker) => worker.findDocumentSymbols(String(resource)))
|
||||||
|
.then((items) => {
|
||||||
return this._worker(resource)
|
if (!items) {
|
||||||
.then((worker) => worker.findDocumentSymbols(String(resource)))
|
return;
|
||||||
.then((items) => {
|
}
|
||||||
if (!items) {
|
return items.map((item) => toDocumentSymbol(item));
|
||||||
return;
|
});
|
||||||
}
|
},
|
||||||
return items.map((item) => toDocumentSymbol(item));
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromFormattingOptions(
|
function fromFormattingOptions(
|
||||||
|
|
@ -422,22 +425,21 @@ function fromFormattingOptions(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DocumentFormattingEditProvider implements languages.DocumentFormattingEditProvider {
|
export function createDocumentFormattingEditProvider(
|
||||||
constructor(private _worker: WorkerAccessor) {}
|
getWorker: WorkerAccessor,
|
||||||
|
): languages.DocumentFormattingEditProvider {
|
||||||
|
return {
|
||||||
|
provideDocumentFormattingEdits(model, options) {
|
||||||
|
const resource = model.uri;
|
||||||
|
|
||||||
provideDocumentFormattingEdits(
|
return getWorker(resource).then((worker) =>
|
||||||
model: editor.IReadOnlyModel,
|
worker.format(String(resource), fromFormattingOptions(options)).then((edits) => {
|
||||||
options: languages.FormattingOptions,
|
if (!edits || edits.length === 0) {
|
||||||
): PromiseLike<editor.ISingleEditOperation[]> {
|
return;
|
||||||
const resource = model.uri;
|
}
|
||||||
|
return edits.map(toTextEdit);
|
||||||
return this._worker(resource).then((worker) =>
|
}),
|
||||||
worker.format(String(resource), fromFormattingOptions(options)).then((edits) => {
|
);
|
||||||
if (!edits || edits.length === 0) {
|
},
|
||||||
return;
|
};
|
||||||
}
|
|
||||||
return edits.map(toTextEdit);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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';
|
import { setupMode } from './yamlMode';
|
||||||
|
|
||||||
// --- YAML configuration and defaults ---------
|
// --- YAML configuration and defaults ---------
|
||||||
|
|
||||||
export class LanguageServiceDefaultsImpl implements languages.yaml.LanguageServiceDefaults {
|
export function createLanguageServiceDefaults(
|
||||||
private _onDidChange = new Emitter<languages.yaml.LanguageServiceDefaults>();
|
languageId: string,
|
||||||
private _diagnosticsOptions: languages.yaml.DiagnosticsOptions;
|
initialDiagnosticsOptions: languages.yaml.DiagnosticsOptions,
|
||||||
private _languageId: string;
|
): languages.yaml.LanguageServiceDefaults {
|
||||||
|
const onDidChange = new Emitter<languages.yaml.LanguageServiceDefaults>();
|
||||||
|
let diagnosticsOptions = initialDiagnosticsOptions;
|
||||||
|
|
||||||
constructor(languageId: string, diagnosticsOptions: languages.yaml.DiagnosticsOptions) {
|
const languageServiceDefaults: languages.yaml.LanguageServiceDefaults = {
|
||||||
this._languageId = languageId;
|
get onDidChange() {
|
||||||
this.setDiagnosticsOptions(diagnosticsOptions);
|
return onDidChange.event;
|
||||||
}
|
},
|
||||||
|
|
||||||
get onDidChange(): IEvent<languages.yaml.LanguageServiceDefaults> {
|
get languageId() {
|
||||||
return this._onDidChange.event;
|
return languageId;
|
||||||
}
|
},
|
||||||
|
|
||||||
get languageId(): string {
|
get diagnosticsOptions() {
|
||||||
return this._languageId;
|
return diagnosticsOptions;
|
||||||
}
|
},
|
||||||
|
|
||||||
get diagnosticsOptions(): languages.yaml.DiagnosticsOptions {
|
setDiagnosticsOptions(options) {
|
||||||
return this._diagnosticsOptions;
|
diagnosticsOptions = options || {};
|
||||||
}
|
onDidChange.fire(languageServiceDefaults);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
setDiagnosticsOptions(options: languages.yaml.DiagnosticsOptions): void {
|
return languageServiceDefaults;
|
||||||
this._diagnosticsOptions = options || Object.create(null);
|
|
||||||
this._onDidChange.fire(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const diagnosticDefault: languages.yaml.DiagnosticsOptions = {
|
const diagnosticDefault: languages.yaml.DiagnosticsOptions = {
|
||||||
|
|
@ -38,7 +39,7 @@ const diagnosticDefault: languages.yaml.DiagnosticsOptions = {
|
||||||
enableSchemaRequest: false,
|
enableSchemaRequest: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const yamlDefaults = new LanguageServiceDefaultsImpl('yaml', diagnosticDefault);
|
const yamlDefaults = createLanguageServiceDefaults('yaml', diagnosticDefault);
|
||||||
|
|
||||||
// Export API
|
// Export API
|
||||||
function createAPI(): typeof languages.yaml {
|
function createAPI(): typeof languages.yaml {
|
||||||
|
|
|
||||||
|
|
@ -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';
|
import { YAMLWorker } from './yamlWorker';
|
||||||
|
|
||||||
|
export interface WorkerManager extends IDisposable {
|
||||||
|
getLanguageServiceWorker: (...resources: Uri[]) => Promise<YAMLWorker>;
|
||||||
|
}
|
||||||
|
|
||||||
// 2min
|
// 2min
|
||||||
const STOP_WHEN_IDLE_FOR = 2 * 60 * 1000;
|
const STOP_WHEN_IDLE_FOR = 2 * 60 * 1000;
|
||||||
|
|
||||||
export class WorkerManager {
|
export function createWorkerManager(
|
||||||
private _defaults: LanguageServiceDefaultsImpl;
|
defaults: languages.yaml.LanguageServiceDefaults,
|
||||||
private _idleCheckInterval: number;
|
): WorkerManager {
|
||||||
private _lastUsedTime: number;
|
let worker: editor.MonacoWebWorker<YAMLWorker>;
|
||||||
private _configChangeListener: IDisposable;
|
let client: Promise<YAMLWorker>;
|
||||||
|
let lastUsedTime = 0;
|
||||||
|
|
||||||
private _worker: editor.MonacoWebWorker<YAMLWorker>;
|
const stopWorker = (): void => {
|
||||||
private _client: Promise<YAMLWorker>;
|
if (worker) {
|
||||||
|
worker.dispose();
|
||||||
constructor(defaults: LanguageServiceDefaultsImpl) {
|
worker = null;
|
||||||
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<YAMLWorker> {
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
this._client = null;
|
client = null;
|
||||||
}
|
};
|
||||||
|
|
||||||
private _checkIfIdle(): void {
|
const idleCheckInterval = setInterval(() => {
|
||||||
if (!this._worker) {
|
if (!worker) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const timePassedSinceLastUsed = Date.now() - this._lastUsedTime;
|
const timePassedSinceLastUsed = Date.now() - lastUsedTime;
|
||||||
if (timePassedSinceLastUsed > STOP_WHEN_IDLE_FOR) {
|
if (timePassedSinceLastUsed > STOP_WHEN_IDLE_FOR) {
|
||||||
this._stopWorker();
|
stopWorker();
|
||||||
}
|
}
|
||||||
}
|
}, 30 * 1000);
|
||||||
|
|
||||||
private _getClient(): Promise<YAMLWorker> {
|
const configChangeListener = defaults.onDidChange(() => stopWorker());
|
||||||
this._lastUsedTime = Date.now();
|
|
||||||
|
|
||||||
if (!this._client) {
|
const getClient = (): Promise<YAMLWorker> => {
|
||||||
this._worker = editor.createWebWorker<YAMLWorker>({
|
lastUsedTime = Date.now();
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
worker = editor.createWebWorker<YAMLWorker>({
|
||||||
// Module that exports the create() method and returns a `YAMLWorker` instance
|
// Module that exports the create() method and returns a `YAMLWorker` instance
|
||||||
moduleId: 'vs/language/yaml/yamlWorker',
|
moduleId: 'vs/language/yaml/yamlWorker',
|
||||||
|
|
||||||
label: this._defaults.languageId,
|
label: defaults.languageId,
|
||||||
|
|
||||||
// Passed in to the create() method
|
// Passed in to the create() method
|
||||||
createData: {
|
createData: {
|
||||||
languageSettings: this._defaults.diagnosticsOptions,
|
languageSettings: defaults.diagnosticsOptions,
|
||||||
languageId: this._defaults.languageId,
|
languageId: defaults.languageId,
|
||||||
enableSchemaRequest: this._defaults.diagnosticsOptions.enableSchemaRequest,
|
enableSchemaRequest: defaults.diagnosticsOptions.enableSchemaRequest,
|
||||||
prefix: this._defaults.diagnosticsOptions.prefix,
|
prefix: defaults.diagnosticsOptions.prefix,
|
||||||
isKubernetes: this._defaults.diagnosticsOptions.isKubernetes,
|
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);
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 = () => {
|
self.onmessage = () => {
|
||||||
// Ignore the first message
|
initialize((ctx, createData) => Object.create(createYAMLWorker(ctx, createData)));
|
||||||
worker.initialize((ctx, createData) => new YAMLWorker(ctx, createData));
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
import { IDisposable, languages, Uri } from 'monaco-editor/esm/vs/editor/editor.api';
|
import { IDisposable, languages, Uri } from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
|
|
||||||
import * as languageFeatures from './languageFeatures';
|
import {
|
||||||
import { LanguageServiceDefaultsImpl } from './monaco.contribution';
|
createCompletionItemProvider,
|
||||||
import { WorkerManager } from './workerManager';
|
createDiagnosticsAdapter,
|
||||||
|
createDocumentFormattingEditProvider,
|
||||||
|
createDocumentSymbolProvider,
|
||||||
|
createHoverProvider,
|
||||||
|
WorkerAccessor,
|
||||||
|
} from './languageFeatures';
|
||||||
|
import { createWorkerManager } from './workerManager';
|
||||||
import { YAMLWorker } from './yamlWorker';
|
import { YAMLWorker } from './yamlWorker';
|
||||||
|
|
||||||
const richEditConfiguration: languages.LanguageConfiguration = {
|
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 disposables: IDisposable[] = [];
|
||||||
|
|
||||||
const client = new WorkerManager(defaults);
|
const client = createWorkerManager(defaults);
|
||||||
disposables.push(client);
|
disposables.push(client);
|
||||||
|
|
||||||
const worker: languageFeatures.WorkerAccessor = (...uris: Uri[]): Promise<YAMLWorker> =>
|
const worker: WorkerAccessor = (...uris: Uri[]): Promise<YAMLWorker> =>
|
||||||
client.getLanguageServiceWorker(...uris);
|
client.getLanguageServiceWorker(...uris);
|
||||||
|
|
||||||
const { languageId } = defaults;
|
const { languageId } = defaults;
|
||||||
|
|
||||||
disposables.push(
|
disposables.push(
|
||||||
languages.registerCompletionItemProvider(
|
languages.registerCompletionItemProvider(languageId, createCompletionItemProvider(worker)),
|
||||||
languageId,
|
languages.registerHoverProvider(languageId, createHoverProvider(worker)),
|
||||||
new languageFeatures.CompletionAdapter(worker),
|
languages.registerDocumentSymbolProvider(languageId, createDocumentSymbolProvider(worker)),
|
||||||
),
|
|
||||||
languages.registerHoverProvider(languageId, new languageFeatures.HoverAdapter(worker)),
|
|
||||||
languages.registerDocumentSymbolProvider(
|
|
||||||
languageId,
|
|
||||||
new languageFeatures.DocumentSymbolAdapter(worker),
|
|
||||||
),
|
|
||||||
languages.registerDocumentFormattingEditProvider(
|
languages.registerDocumentFormattingEditProvider(
|
||||||
languageId,
|
languageId,
|
||||||
new languageFeatures.DocumentFormattingEditProvider(worker),
|
createDocumentFormattingEditProvider(worker),
|
||||||
),
|
),
|
||||||
new languageFeatures.DiagnosticsAdapter(languageId, worker, defaults),
|
createDiagnosticsAdapter(languageId, worker, defaults),
|
||||||
languages.setLanguageConfiguration(languageId, richEditConfiguration),
|
languages.setLanguageConfiguration(languageId, richEditConfiguration),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import { worker } from 'monaco-editor/esm/vs/editor/editor.api';
|
import { worker } from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
import * as ls from 'vscode-languageserver-types';
|
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<string>;
|
let defaultSchemaRequestService: (url: string) => PromiseLike<string>;
|
||||||
|
|
||||||
|
|
@ -8,90 +12,96 @@ if (typeof fetch !== 'undefined') {
|
||||||
defaultSchemaRequestService = (url) => fetch(url).then((response) => response.text());
|
defaultSchemaRequestService = (url) => fetch(url).then((response) => response.text());
|
||||||
}
|
}
|
||||||
|
|
||||||
export class YAMLWorker {
|
export interface YAMLWorker {
|
||||||
private _ctx: worker.IWorkerContext;
|
doValidation: (uri: string) => PromiseLike<ls.Diagnostic[]>;
|
||||||
private _languageService: yamlService.LanguageService;
|
|
||||||
private _languageSettings: yamlService.LanguageSettings;
|
|
||||||
private _languageId: string;
|
|
||||||
private _isKubernetes: boolean;
|
|
||||||
|
|
||||||
constructor(ctx: worker.IWorkerContext, createData: ICreateData) {
|
doComplete: (uri: string, position: ls.Position) => PromiseLike<ls.CompletionList>;
|
||||||
const prefix = createData.prefix || '';
|
|
||||||
const service = (url: string): PromiseLike<string> =>
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
doValidation(uri: string): PromiseLike<ls.Diagnostic[]> {
|
doResolve: (item: ls.CompletionItem) => PromiseLike<ls.CompletionItem>;
|
||||||
const document = this._getTextDocument(uri);
|
|
||||||
if (document) {
|
|
||||||
return this._languageService.doValidation(document, this._isKubernetes);
|
|
||||||
}
|
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
doComplete(uri: string, position: ls.Position): PromiseLike<ls.CompletionList> {
|
doHover: (uri: string, position: ls.Position) => PromiseLike<ls.Hover>;
|
||||||
const document = this._getTextDocument(uri);
|
|
||||||
return this._languageService.doComplete(document, position, this._isKubernetes);
|
|
||||||
}
|
|
||||||
|
|
||||||
doResolve(item: ls.CompletionItem): PromiseLike<ls.CompletionItem> {
|
format: (uri: string, options: CustomFormatterOptions) => PromiseLike<ls.TextEdit[]>;
|
||||||
return this._languageService.doResolve(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
doHover(uri: string, position: ls.Position): PromiseLike<ls.Hover> {
|
resetSchema: (uri: string) => PromiseLike<boolean>;
|
||||||
const document = this._getTextDocument(uri);
|
|
||||||
return this._languageService.doHover(document, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
format(uri: string, options: yamlService.CustomFormatterOptions): PromiseLike<ls.TextEdit[]> {
|
findDocumentSymbols: (uri: string) => PromiseLike<ls.DocumentSymbol[]>;
|
||||||
const document = this._getTextDocument(uri);
|
}
|
||||||
const textEdits = this._languageService.doFormat(document, options);
|
|
||||||
return Promise.resolve(textEdits);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetSchema(uri: string): PromiseLike<boolean> {
|
export function createYAMLWorker(
|
||||||
return Promise.resolve(this._languageService.resetSchema(uri));
|
ctx: worker.IWorkerContext,
|
||||||
}
|
{
|
||||||
|
enableSchemaRequest,
|
||||||
|
isKubernetes = false,
|
||||||
|
languageId,
|
||||||
|
languageSettings,
|
||||||
|
prefix = '',
|
||||||
|
}: ICreateData,
|
||||||
|
): YAMLWorker {
|
||||||
|
const service = (url: string): PromiseLike<string> =>
|
||||||
|
defaultSchemaRequestService(`${prefix}${url}`);
|
||||||
|
const languageService = getLanguageService(enableSchemaRequest && service, null, []);
|
||||||
|
languageService.configure({
|
||||||
|
...languageSettings,
|
||||||
|
hover: true,
|
||||||
|
isKubernetes,
|
||||||
|
});
|
||||||
|
|
||||||
findDocumentSymbols(uri: string): PromiseLike<ls.DocumentSymbol[]> {
|
const getTextDocument = (uri: string): ls.TextDocument => {
|
||||||
const document = this._getTextDocument(uri);
|
const models = ctx.getMirrorModels();
|
||||||
const symbols = this._languageService.findDocumentSymbols2(document);
|
|
||||||
return Promise.resolve(symbols);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getTextDocument(uri: string): ls.TextDocument {
|
|
||||||
const models = this._ctx.getMirrorModels();
|
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
if (String(model.uri) === uri) {
|
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 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 {
|
export interface ICreateData {
|
||||||
languageId: string;
|
languageId: string;
|
||||||
languageSettings: yamlService.LanguageSettings;
|
languageSettings: LanguageSettings;
|
||||||
enableSchemaRequest: boolean;
|
enableSchemaRequest: boolean;
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
isKubernetes?: boolean;
|
isKubernetes?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function create(ctx: worker.IWorkerContext, createData: ICreateData): YAMLWorker {
|
|
||||||
return new YAMLWorker(ctx, createData);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue