From ad350d08b28d8fc4f75b17bda0a025487ef2cd0f Mon Sep 17 00:00:00 2001 From: Kevin Decker Date: Sun, 25 Feb 2018 23:35:44 -0600 Subject: [PATCH] Convert json implementation to yaml libs --- gulpfile.js | 52 +++---- package.json | 2 +- src/jsonWorker.ts | 116 -------------- src/languageFeatures.ts | 16 +- src/monaco.contribution.ts | 43 +++--- src/monaco.d.ts | 8 +- src/workerManager.ts | 20 +-- .../services/yamlCompletion.ts | 31 ++-- .../services/yamlFormatter.ts | 2 +- src/{jsonMode.ts => yamlMode.ts} | 46 ++++-- src/yamlWorker.ts | 146 ++++++++++++++++++ 11 files changed, 255 insertions(+), 227 deletions(-) delete mode 100644 src/jsonWorker.ts rename src/{jsonMode.ts => yamlMode.ts} (67%) create mode 100644 src/yamlWorker.ts diff --git a/gulpfile.js b/gulpfile.js index 1cc499d..7f8920f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -14,8 +14,8 @@ var uglify = require('gulp-uglify'); var rimraf = require('rimraf'); var es = require('event-stream'); -gulp.task('clean-release', function(cb) { rimraf('release', { maxBusyTries: 1 }, cb); }); -gulp.task('release', ['clean-release','compile'], function() { +gulp.task('clean-release', function (cb) { rimraf('release', { maxBusyTries: 1 }, cb); }); +gulp.task('release', ['clean-release', 'compile'], function () { var sha1 = getGitVersion(__dirname); var semver = require('./package.json').version; @@ -52,21 +52,13 @@ gulp.task('release', ['clean-release','compile'], function() { return rjs({ baseUrl: '/out/', - name: 'vs/language/json/' + moduleId, + name: 'hl/yaml/' + moduleId, out: moduleId + '.js', exclude: exclude, paths: { - 'vs/language/json': __dirname + '/out' + 'hl/yaml': __dirname + '/out' }, packages: [{ - name: 'vscode-yaml-languageservice', - location: __dirname + '/out/vscode-yaml-languageservice/', - main: 'yamlLanguageService' - }, { - name: 'yaml-language-server', - location: __dirname + '/out/yaml-language-server', - main: 'yamlLanguageService' - }, { name: 'yaml-ast-parser', location: __dirname + '/out/yaml-ast-parser', main: 'index' @@ -108,22 +100,22 @@ gulp.task('release', ['clean-release','compile'], function() { return merge( merge( - bundleOne('monaco.contribution', ['vs/language/json/jsonMode']), - bundleOne('jsonMode'), - bundleOne('jsonWorker') + bundleOne('monaco.contribution', ['hl/yaml/yamlMode']), + bundleOne('yamlMode'), + bundleOne('yamlWorker') ) - .pipe(es.through(function(data) { - data.contents = new Buffer( - BUNDLED_FILE_HEADER - + data.contents.toString() - ); - this.emit('data', data); - })) - .pipe(gulp.dest('./release/dev')) - .pipe(uglify({ - preserveComments: 'some' - })) - .pipe(gulp.dest('./release/min')), + .pipe(es.through(function (data) { + data.contents = new Buffer( + BUNDLED_FILE_HEADER + + data.contents.toString() + ); + this.emit('data', data); + })) + .pipe(gulp.dest('./release/dev')) + .pipe(uglify({ + preserveComments: 'some' + })) + .pipe(gulp.dest('./release/min')), gulp.src('src/monaco.d.ts').pipe(gulp.dest('./release/min')) ); }); @@ -137,13 +129,13 @@ function compileTask() { return merge( gulp.src(tsSources).pipe(compilation()) ) - .pipe(gulp.dest('out')); + .pipe(gulp.dest('out')); } -gulp.task('clean-out', function(cb) { rimraf('out', { maxBusyTries: 1 }, cb); }); +gulp.task('clean-out', function (cb) { rimraf('out', { maxBusyTries: 1 }, cb); }); gulp.task('compile', ['clean-out'], compileTask); gulp.task('compile-without-clean', compileTask); -gulp.task('watch', ['compile'], function() { +gulp.task('watch', ['compile'], function () { gulp.watch(tsSources, ['compile-without-clean']); }); diff --git a/package.json b/package.json index 7b8a9f7..a622ff2 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "monaco-json", + "name": "monaco-yaml", "version": "1.3.2", "description": "JSON plugin for the Monaco Editor", "scripts": { diff --git a/src/jsonWorker.ts b/src/jsonWorker.ts deleted file mode 100644 index d9b52cf..0000000 --- a/src/jsonWorker.ts +++ /dev/null @@ -1,116 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import Promise = monaco.Promise; -import Thenable = monaco.Thenable; -import IWorkerContext = monaco.worker.IWorkerContext; - -import * as jsonService from 'vscode-json-languageservice'; -import * as ls from 'vscode-languageserver-types'; - -class PromiseAdapter implements jsonService.Thenable { - private wrapped: monaco.Promise; - - constructor(executor: (resolve: (value?: T | jsonService.Thenable) => void, reject: (reason?: any) => void) => void) { - this.wrapped = new monaco.Promise(executor); - } - public then(onfulfilled?: (value: T) => TResult | jsonService.Thenable, onrejected?: (reason: any) => void): jsonService.Thenable { - let thenable : monaco.Thenable = this.wrapped; - return thenable.then(onfulfilled, onrejected); - } - public getWrapped(): monaco.Thenable { - return this.wrapped; - } - public cancel(): void { - this.wrapped.cancel(); - } - public static resolve(v: T | Thenable): jsonService.Thenable { - return > monaco.Promise.as(v); - } - public static reject(v: T): jsonService.Thenable { - return monaco.Promise.wrapError(v); - } - public static all(values: jsonService.Thenable[]): jsonService.Thenable { - return monaco.Promise.join(values); - } -} - -function toMonacoPromise(thenable: jsonService.Thenable): Thenable { - if (thenable instanceof PromiseAdapter) { - return thenable.getWrapped(); - } - return thenable; -} - -export class JSONWorker { - - private _ctx: IWorkerContext; - private _languageService: jsonService.LanguageService; - private _languageSettings: jsonService.LanguageSettings; - private _languageId: string; - - constructor(ctx: IWorkerContext, createData: ICreateData) { - this._ctx = ctx; - this._languageSettings = createData.languageSettings; - this._languageId = createData.languageId; - this._languageService = jsonService.getLanguageService({ promiseConstructor: PromiseAdapter }); - this._languageService.configure(this._languageSettings); - } - - doValidation(uri: string): Thenable { - let document = this._getTextDocument(uri); - if (document) { - let jsonDocument = this._languageService.parseJSONDocument(document); - return this._languageService.doValidation(document, jsonDocument); - } - return Promise.as([]); - } - doComplete(uri: string, position: ls.Position): Thenable { - let document = this._getTextDocument(uri); - let jsonDocument = this._languageService.parseJSONDocument(document); - return this._languageService.doComplete(document, position, jsonDocument); - } - doResolve(item: ls.CompletionItem): Thenable { - return this._languageService.doResolve(item); - } - doHover(uri: string, position: ls.Position): Thenable { - let document = this._getTextDocument(uri); - let jsonDocument = this._languageService.parseJSONDocument(document); - return this._languageService.doHover(document, position, jsonDocument); - } - format(uri: string, range: ls.Range, options: ls.FormattingOptions): Thenable { - let document = this._getTextDocument(uri); - let textEdits = this._languageService.format(document, range, options); - return Promise.as(textEdits); - } - resetSchema(uri: string): Thenable { - return Promise.as(this._languageService.resetSchema(uri)); - } - findDocumentSymbols(uri: string): Promise { - let document = this._getTextDocument(uri); - let jsonDocument = this._languageService.parseJSONDocument(document); - let symbols = this._languageService.findDocumentSymbols(document, jsonDocument); - return Promise.as(symbols); - } - private _getTextDocument(uri: string): ls.TextDocument { - let models = this._ctx.getMirrorModels(); - for (let model of models) { - if (model.uri.toString() === uri) { - return ls.TextDocument.create(uri, this._languageId, model.version, model.getValue()); - } - } - return null; - } -} - -export interface ICreateData { - languageId: string; - languageSettings: jsonService.LanguageSettings; -} - -export function create(ctx: IWorkerContext, createData: ICreateData): JSONWorker { - return new JSONWorker(ctx, createData); -} diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index a40a3fc..97afcbd 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import {LanguageServiceDefaultsImpl} from './monaco.contribution'; -import {JSONWorker} from './jsonWorker'; +import { LanguageServiceDefaultsImpl } from './monaco.contribution'; +import { YAMLWorker } from './yamlWorker'; import * as ls from 'vscode-languageserver-types'; @@ -19,7 +19,7 @@ import IDisposable = monaco.IDisposable; export interface WorkerAccessor { - (...more: Uri[]): Thenable + (...more: Uri[]): Thenable } // --- diagnostics --- --- @@ -234,7 +234,7 @@ function toCompletionItem(entry: ls.CompletionItem): DataCompletionItem { } function fromCompletionItem(entry: DataCompletionItem): ls.CompletionItem { - let item : ls.CompletionItem = { + let item: ls.CompletionItem = { label: entry.label, sortText: entry.sortText, filterText: entry.filterText, @@ -247,7 +247,7 @@ function fromCompletionItem(entry: DataCompletionItem): ls.CompletionItem { item.insertText = entry.insertText.value; item.insertTextFormat = ls.InsertTextFormat.Snippet } else { - item.insertText = entry.insertText; + item.insertText = entry.insertText; } if (entry.range) { item.textEdit = ls.TextEdit.replace(fromRange(entry.range), item.insertText); @@ -276,7 +276,7 @@ export class CompletionAdapter implements monaco.languages.CompletionItemProvide return; } let items: monaco.languages.CompletionItem[] = info.items.map(entry => { - let item : monaco.languages.CompletionItem = { + let item: monaco.languages.CompletionItem = { label: entry.label, insertText: entry.insertText, sortText: entry.sortText, @@ -290,7 +290,7 @@ export class CompletionAdapter implements monaco.languages.CompletionItemProvide item.insertText = entry.textEdit.newText; } if (entry.insertTextFormat === ls.InsertTextFormat.Snippet) { - item.insertText = { value: item.insertText }; + item.insertText = { value: item.insertText }; } return item; }); @@ -403,7 +403,7 @@ export class DocumentSymbolAdapter implements monaco.languages.DocumentSymbolPro function fromFormattingOptions(options: monaco.languages.FormattingOptions): ls.FormattingOptions { return { tabSize: options.tabSize, - insertSpaces: options.insertSpaces + insertSpaces: options.insertSpaces }; } diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index 4794356..78a7b02 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as mode from './jsonMode'; +import * as mode from './yamlMode'; import Emitter = monaco.Emitter; import IEvent = monaco.IEvent; @@ -14,18 +14,18 @@ declare var require: (moduleId: [string], callback: (module: T) => void) => v // --- JSON configuration and defaults --------- -export class LanguageServiceDefaultsImpl implements monaco.languages.json.LanguageServiceDefaults { +export class LanguageServiceDefaultsImpl implements monaco.languages.yaml.LanguageServiceDefaults { - private _onDidChange = new Emitter(); - private _diagnosticsOptions: monaco.languages.json.DiagnosticsOptions; + private _onDidChange = new Emitter(); + private _diagnosticsOptions: monaco.languages.yaml.DiagnosticsOptions; private _languageId: string; - constructor(languageId: string, diagnosticsOptions: monaco.languages.json.DiagnosticsOptions) { + constructor(languageId: string, diagnosticsOptions: monaco.languages.yaml.DiagnosticsOptions) { this._languageId = languageId; this.setDiagnosticsOptions(diagnosticsOptions); } - get onDidChange(): IEvent { + get onDidChange(): IEvent { return this._onDidChange.event; } @@ -33,45 +33,42 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.json.Langua return this._languageId; } - get diagnosticsOptions(): monaco.languages.json.DiagnosticsOptions { + get diagnosticsOptions(): monaco.languages.yaml.DiagnosticsOptions { return this._diagnosticsOptions; } - setDiagnosticsOptions(options: monaco.languages.json.DiagnosticsOptions): void { + setDiagnosticsOptions(options: monaco.languages.yaml.DiagnosticsOptions): void { this._diagnosticsOptions = options || Object.create(null); this._onDidChange.fire(this); } } -const diagnosticDefault: monaco.languages.json.DiagnosticsOptions = { +const diagnosticDefault: monaco.languages.yaml.DiagnosticsOptions = { validate: true, - allowComments: true, schemas: [] } -const jsonDefaults = new LanguageServiceDefaultsImpl('json', diagnosticDefault); +const yamlDefaults = new LanguageServiceDefaultsImpl('yaml', diagnosticDefault); // Export API -function createAPI(): typeof monaco.languages.json { +function createAPI(): typeof monaco.languages.yaml { return { - jsonDefaults: jsonDefaults, + yamlDefaults: yamlDefaults, } } -monaco.languages.json = createAPI(); +monaco.languages.yaml = createAPI(); // --- Registration to monaco editor --- function withMode(callback: (module: typeof mode) => void): void { - require(['vs/language/json/jsonMode'], callback); + require(['hl/yaml/yamlMode'], callback); } -monaco.languages.register({ - id: 'json', - extensions: ['.json', '.bowerrc', '.jshintrc', '.jscsrc', '.eslintrc', '.babelrc'], - aliases: ['JSON', 'json'], - mimetypes: ['application/json'], -}); -monaco.languages.onLanguage('json', () => { - withMode(mode => mode.setupMode(jsonDefaults)); +// monaco.languages.register({ +// id: 'yaml', +// extensions: ['.yml'], +// }); +monaco.languages.onLanguage('yaml', () => { + withMode(mode => mode.setupMode(yamlDefaults)); }); diff --git a/src/monaco.d.ts b/src/monaco.d.ts index d79f3a3..875ec6d 100644 --- a/src/monaco.d.ts +++ b/src/monaco.d.ts @@ -3,16 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -declare module monaco.languages.json { +declare module monaco.languages.yaml { export interface DiagnosticsOptions { /** * If set, the validator will be enabled and perform syntax validation as well as schema based validation. */ readonly validate?: boolean; - /** - * If set, comments are tolerated. If set to false, syntax errors will be emmited for comments. - */ - readonly allowComments?: boolean; /** * A list of known schemas and/or associations of schemas to file names. */ @@ -38,5 +34,5 @@ declare module monaco.languages.json { setDiagnosticsOptions(options: DiagnosticsOptions): void; } - export var jsonDefaults: LanguageServiceDefaults; + export var yamlDefaults: LanguageServiceDefaults; } \ No newline at end of file diff --git a/src/workerManager.ts b/src/workerManager.ts index def95b7..43d025b 100644 --- a/src/workerManager.ts +++ b/src/workerManager.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import {LanguageServiceDefaultsImpl} from './monaco.contribution'; -import {JSONWorker} from './jsonWorker'; +import { LanguageServiceDefaultsImpl } from './monaco.contribution'; +import { YAMLWorker } from './yamlWorker'; import Promise = monaco.Promise; import IDisposable = monaco.IDisposable; @@ -20,8 +20,8 @@ export class WorkerManager { private _lastUsedTime: number; private _configChangeListener: IDisposable; - private _worker: monaco.editor.MonacoWebWorker; - private _client: Promise; + private _worker: monaco.editor.MonacoWebWorker; + private _client: Promise; constructor(defaults: LanguageServiceDefaultsImpl) { this._defaults = defaults; @@ -55,14 +55,14 @@ export class WorkerManager { } } - private _getClient(): Promise { + private _getClient(): Promise { this._lastUsedTime = Date.now(); if (!this._client) { - this._worker = monaco.editor.createWebWorker({ + this._worker = monaco.editor.createWebWorker({ - // module that exports the create() method and returns a `JSONWorker` instance - moduleId: 'vs/language/json/jsonWorker', + // module that exports the create() method and returns a `YAMLWorker` instance + moduleId: 'hl/yaml/yamlWorker', label: this._defaults.languageId, @@ -79,8 +79,8 @@ export class WorkerManager { return this._client; } - getLanguageServiceWorker(...resources: Uri[]): Promise { - let _client: JSONWorker; + getLanguageServiceWorker(...resources: Uri[]): Promise { + let _client: YAMLWorker; return toShallowCancelPromise( this._getClient().then((client) => { _client = client diff --git a/src/yaml-languageservice/services/yamlCompletion.ts b/src/yaml-languageservice/services/yamlCompletion.ts index eb5c8bb..96f5e6a 100644 --- a/src/yaml-languageservice/services/yamlCompletion.ts +++ b/src/yaml-languageservice/services/yamlCompletion.ts @@ -52,12 +52,12 @@ export class YAMLCompletion { }; let offset = document.offsetAt(position); - if(document.getText()[offset] === ":"){ + if (document.getText()[offset] === ":") { return null; } - + let currentDoc = matchOffsetToDocument(offset, doc); - if(currentDoc === null){ + if (currentDoc === null) { return null; } let node = currentDoc.getNodeFromOffsetEndInclusive(offset); @@ -94,7 +94,7 @@ export class YAMLCompletion { return this.schemaService.getSchemaForResource(document.uri).then((schema) => { - if(!schema){ + if (!schema) { return null; } @@ -132,7 +132,7 @@ export class YAMLCompletion { if (schema) { // property proposals with schema this.getPropertyCompletions(schema, currentDoc, node, addValue, collector); - } + } let location = node.getPath(); this.contributions.forEach((contribution) => { @@ -153,7 +153,7 @@ export class YAMLCompletion { let types: { [type: string]: boolean } = {}; if (schema) { this.getValueCompletions(schema, currentDoc, node, offset, document, collector, types); - } + } if (this.contributions.length > 0) { this.getContributedValueCompletions(currentDoc, node, offset, document, collector, collectionPromises); } @@ -176,6 +176,7 @@ export class YAMLCompletion { collector.add({ kind: CompletionItemKind.Property, label: key, + insertText: `${key}:`, filterText: this.getFilterTextForValue(key), documentation: propertySchema.description || '' }); @@ -190,24 +191,24 @@ export class YAMLCompletion { let offsetForSeparator = offset; let parentKey: string = null; let valueNode: Parser.ASTNode = null; - + if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean')) { offsetForSeparator = node.end; valueNode = node; node = node.parent; } - if(node && node.type === 'null'){ + if (node && node.type === 'null') { let nodeParent = node.parent; - + /* * This is going to be an object for some reason and we need to find the property * Its an issue with the null node */ - if(nodeParent && nodeParent.type === "object"){ - for(let prop in nodeParent["properties"]){ + if (nodeParent && nodeParent.type === "object") { + for (let prop in nodeParent["properties"]) { let currNode = nodeParent["properties"][prop]; - if(currNode.key && currNode.key.location === node.location){ + if (currNode.key && currNode.key.location === node.location) { node = currNode; } } @@ -218,7 +219,7 @@ export class YAMLCompletion { this.addSchemaValueCompletions(schema.schema, collector, types); return; } - + if ((node.type === 'property') && offset > (node).colonOffset) { let propertyNode = node; let valueNode = propertyNode.value; @@ -252,7 +253,7 @@ export class YAMLCompletion { } }); } - if(node){ + if (node) { if (types['boolean']) { this.addBooleanValueCompletion(true, collector); this.addBooleanValueCompletion(false, collector); @@ -260,7 +261,7 @@ export class YAMLCompletion { if (types['null']) { this.addNullValueCompletion(collector); } - } + } } private getContributedValueCompletions(doc: Parser.JSONDocument, node: Parser.ASTNode, offset: number, document: TextDocument, collector: CompletionsCollector, collectionPromises: Thenable[]) { diff --git a/src/yaml-languageservice/services/yamlFormatter.ts b/src/yaml-languageservice/services/yamlFormatter.ts index c867d9f..eb8b80e 100644 --- a/src/yaml-languageservice/services/yamlFormatter.ts +++ b/src/yaml-languageservice/services/yamlFormatter.ts @@ -1,6 +1,6 @@ 'use strict'; -import jsyaml = require('js-yaml') +import * as jsyaml from 'js-yaml' import { EOL } from 'os'; import { TextDocument, Range, Position, FormattingOptions, TextEdit } from 'vscode-languageserver-types'; diff --git a/src/jsonMode.ts b/src/yamlMode.ts similarity index 67% rename from src/jsonMode.ts rename to src/yamlMode.ts index a304745..ab02d27 100644 --- a/src/jsonMode.ts +++ b/src/yamlMode.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import {WorkerManager} from './workerManager'; -import {JSONWorker} from './jsonWorker'; -import {LanguageServiceDefaultsImpl} from './monaco.contribution'; +import { WorkerManager } from './workerManager'; +import { YAMLWorker } from './yamlWorker'; +import { LanguageServiceDefaultsImpl } from './monaco.contribution'; import * as languageFeatures from './languageFeatures'; -import {createTokenizationSupport} from './tokenization'; +// import { createTokenizationSupport } from './tokenization'; import Promise = monaco.Promise; import Uri = monaco.Uri; @@ -21,7 +21,7 @@ export function setupMode(defaults: LanguageServiceDefaultsImpl): void { const client = new WorkerManager(defaults); disposables.push(client); - const worker: languageFeatures.WorkerAccessor = (...uris: Uri[]): Promise => { + const worker: languageFeatures.WorkerAccessor = (...uris: Uri[]): Promise => { return client.getLanguageServiceWorker(...uris); }; @@ -33,28 +33,40 @@ export function setupMode(defaults: LanguageServiceDefaultsImpl): void { disposables.push(monaco.languages.registerDocumentFormattingEditProvider(languageId, new languageFeatures.DocumentFormattingEditProvider(worker))); disposables.push(monaco.languages.registerDocumentRangeFormattingEditProvider(languageId, new languageFeatures.DocumentRangeFormattingEditProvider(worker))); disposables.push(new languageFeatures.DiagnostcsAdapter(languageId, worker)); - disposables.push(monaco.languages.setTokensProvider(languageId, createTokenizationSupport(true))); + // disposables.push(monaco.languages.setTokensProvider(languageId, createTokenizationSupport(true))); disposables.push(monaco.languages.setLanguageConfiguration(languageId, richEditConfiguration)); } const richEditConfiguration: monaco.languages.LanguageConfiguration = { - wordPattern: /(-?\d*\.\d\w*)|([^\[\{\]\}\:\"\,\s]+)/g, - comments: { - lineComment: '//', - blockComment: ['/*', '*/'] + lineComment: '#' }, - brackets: [ ['{', '}'], - ['[', ']'] + ['[', ']'], + ['(', ')'] + ], + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: '\'', close: '\'' }, + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: '\'', close: '\'' }, ], - autoClosingPairs: [ - { open: '{', close: '}', notIn: ['string'] }, - { open: '[', close: ']', notIn: ['string'] }, - { open: '"', close: '"', notIn: ['string'] } - ] + onEnterRules: [ + { + beforeText: /:\s*$/, + action: { indentAction: monaco.languages.IndentAction.Indent } + } + ], }; diff --git a/src/yamlWorker.ts b/src/yamlWorker.ts new file mode 100644 index 0000000..a9f5c72 --- /dev/null +++ b/src/yamlWorker.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Red Hat, Inc. All rights reserved. + * Copyright (c) Adam Voss. All rights reserved. + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Promise = monaco.Promise; +import Thenable = monaco.Thenable; +import IWorkerContext = monaco.worker.IWorkerContext; + +import * as yamlService from './yaml-languageservice/yamlLanguageService'; +import * as ls from 'vscode-languageserver-types'; +import { getLineOffsets } from './yaml-languageservice/utils/arrUtils'; +import { parse as parseYAML } from "./yaml-languageservice/parser/yamlParser"; + +export class YAMLWorker { + + private _ctx: IWorkerContext; + private _languageService: yamlService.LanguageService; + private _languageSettings: yamlService.LanguageSettings; + private _languageId: string; + + constructor(ctx: IWorkerContext, createData: ICreateData) { + this._ctx = ctx; + this._languageSettings = createData.languageSettings; + this._languageId = createData.languageId; + this._languageService = yamlService.getLanguageService(); + this._languageService.configure(this._languageSettings); + } + + doValidation(uri: string): Thenable { + let document = this._getTextDocument(uri); + if (document) { + let jsonDocument = this._languageService.parseYAMLDocument(document); + return this._languageService.doValidation(document, jsonDocument); + } + return Promise.as([]); + } + doComplete(uri: string, position: ls.Position): Thenable { + let document = this._getTextDocument(uri); + let completionFix = completionHelper(document, position); + let newText = completionFix.newText; + let jsonDocument = parseYAML(newText); + return this._languageService.doComplete(document, position, jsonDocument); + } + doResolve(item: ls.CompletionItem): Thenable { + return this._languageService.doResolve(item); + } + doHover(uri: string, position: ls.Position): Thenable { + let document = this._getTextDocument(uri); + let jsonDocument = this._languageService.parseYAMLDocument(document); + return this._languageService.doHover(document, position, jsonDocument); + } + format(uri: string, range: ls.Range, options: ls.FormattingOptions): Thenable { + let document = this._getTextDocument(uri); + let textEdits = this._languageService.format(document, options); + return Promise.as(textEdits); + } + resetSchema(uri: string): Thenable { + return Promise.as(this._languageService.resetSchema(uri)); + } + findDocumentSymbols(uri: string): Promise { + let document = this._getTextDocument(uri); + let jsonDocument = this._languageService.parseYAMLDocument(document); + let symbols = this._languageService.findDocumentSymbols(document, jsonDocument); + return Promise.as(symbols); + } + private _getTextDocument(uri: string): ls.TextDocument { + let models = this._ctx.getMirrorModels(); + for (let model of models) { + if (model.uri.toString() === uri) { + return ls.TextDocument.create(uri, this._languageId, model.version, model.getValue()); + } + } + return null; + } +} + +export interface ICreateData { + languageId: string; + languageSettings: yamlService.LanguageSettings; +} + +export function create(ctx: IWorkerContext, createData: ICreateData): YAMLWorker { + return new YAMLWorker(ctx, createData); +} + + +// https://github.com/redhat-developer/yaml-language-server/blob/5e069c0e9d7004d57f1fa6e93df670d4895883d1/src/server.ts#L453 +function completionHelper(document: ls.TextDocument, textDocumentPosition: ls.Position) { + + //Get the string we are looking at via a substring + let linePos = textDocumentPosition.line; + let position = textDocumentPosition; + let lineOffset = getLineOffsets(document.getText()); + let start = lineOffset[linePos]; //Start of where the autocompletion is happening + let end = 0; //End of where the autocompletion is happening + if (lineOffset[linePos + 1]) { + end = lineOffset[linePos + 1]; + } else { + end = document.getText().length; + } + let textLine = document.getText().substring(start, end); + + //Check if the string we are looking at is a node + if (textLine.indexOf(":") === -1) { + //We need to add the ":" to load the nodes + let newText = ""; + + //This is for the empty line case + let trimmedText = textLine.trim(); + if (trimmedText.length === 0 || (trimmedText.length === 1 && trimmedText[0] === '-')) { + //Add a temp node that is in the document but we don't use at all. + if (lineOffset[linePos + 1]) { + newText = document.getText().substring(0, start + (textLine.length - 1)) + "holder:\r\n" + document.getText().substr(end + 2); + } else { + newText = document.getText().substring(0, start + (textLine.length)) + "holder:\r\n" + document.getText().substr(end + 2); + } + //For when missing semi colon case + } else { + //Add a semicolon to the end of the current line so we can validate the node + if (lineOffset[linePos + 1]) { + newText = document.getText().substring(0, start + (textLine.length - 1)) + ":\r\n" + document.getText().substr(end + 2); + } else { + newText = document.getText().substring(0, start + (textLine.length)) + ":\r\n" + document.getText().substr(end + 2); + } + } + + return { + "newText": newText, + "newPosition": textDocumentPosition + } + + } else { + + //All the nodes are loaded + position.character = position.character - 1; + return { + "newText": document.getText(), + "newPosition": position + } + } + +}