mirror of
https://github.com/danbulant/monaco-yaml
synced 2026-06-17 21:41:15 +00:00
Merge pull request #5 from kpdecker/feat/2.0
build: migrate latest json language service implementation
This commit is contained in:
commit
37dae21bbd
27 changed files with 3781 additions and 2726 deletions
14
.editorconfig
Normal file
14
.editorconfig
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Editor configuration, see http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
|
@ -5,5 +5,7 @@
|
|||
"**/node_modules": true,
|
||||
"**/release": true,
|
||||
"**/out": true
|
||||
}
|
||||
},
|
||||
"javascript.preferences.quoteStyle": "single",
|
||||
"typescript.preferences.quoteStyle": "single"
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "monaco-yaml",
|
||||
"version": "1.3.1",
|
||||
"version": "2.0.0",
|
||||
"description": "YAML plugin for the Monaco Editor",
|
||||
"scripts": {
|
||||
"compile": "rimraf ./out && yarn compile:umd && yarn compile:esm",
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
"rimraf": "^2.6.2",
|
||||
"typescript": "^3.1.6",
|
||||
"uglify-es": "^3.3.9",
|
||||
"vscode-json-languageservice": "^3.1.6",
|
||||
"vscode-languageserver-types": "3.12.0"
|
||||
"vscode-languageserver-types": "3.12.0",
|
||||
"yaml-language-server": "^0.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,10 +46,7 @@ function bundleOne(moduleId, exclude) {
|
|||
name: 'jsonc-parser',
|
||||
location: path.join(REPO_ROOT, 'node_modules/jsonc-parser/lib/umd'),
|
||||
main: 'main'
|
||||
}, {
|
||||
name: 'vscode-json-languageservice/lib',
|
||||
location: path.join(REPO_ROOT, 'node_modules/vscode-json-languageservice/lib/umd')
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'vscode-languageserver-types',
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ helpers.packageESM({
|
|||
"jsonc-parser/lib/esm": "jsonc-parser",
|
||||
"vscode-languageserver-types/lib/esm": "vscode-languageserver-types",
|
||||
"vscode-uri/lib/esm": "vscode-uri",
|
||||
"vscode-json-languageservice/lib/esm": "vscode-json-languageservice",
|
||||
// "js-yaml/dist": "js-yaml"
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,432 +1,479 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { LanguageServiceDefaultsImpl } from './monaco.contribution';
|
||||
import { YAMLWorker } from './yamlWorker';
|
||||
|
||||
import * as ls from 'vscode-languageserver-types';
|
||||
|
||||
import Uri = monaco.Uri;
|
||||
import Position = monaco.Position;
|
||||
import Range = monaco.Range;
|
||||
import IRange = monaco.IRange;
|
||||
import Thenable = monaco.Thenable;
|
||||
import CancellationToken = monaco.CancellationToken;
|
||||
import IDisposable = monaco.IDisposable;
|
||||
|
||||
|
||||
export interface WorkerAccessor {
|
||||
(...more: Uri[]): Thenable<YAMLWorker>
|
||||
}
|
||||
|
||||
// --- diagnostics --- ---
|
||||
|
||||
export class DiagnosticsAdapter {
|
||||
|
||||
private _disposables: IDisposable[] = [];
|
||||
private _listener: { [uri: string]: IDisposable } = Object.create(null);
|
||||
|
||||
constructor(private _languageId: string, private _worker: WorkerAccessor, defaults: LanguageServiceDefaultsImpl) {
|
||||
const onModelAdd = (model: monaco.editor.IModel): void => {
|
||||
let modeId = model.getModeId();
|
||||
if (modeId !== this._languageId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let handle: NodeJS.Timer;
|
||||
this._listener[model.uri.toString()] = model.onDidChangeContent(() => {
|
||||
clearTimeout(handle);
|
||||
handle = setTimeout(() => this._doValidate(model.uri, modeId), 500);
|
||||
});
|
||||
|
||||
this._doValidate(model.uri, modeId);
|
||||
};
|
||||
|
||||
const onModelRemoved = (model: monaco.editor.IModel): void => {
|
||||
monaco.editor.setModelMarkers(model, this._languageId, []);
|
||||
let uriStr = model.uri.toString();
|
||||
let listener = this._listener[uriStr];
|
||||
if (listener) {
|
||||
listener.dispose();
|
||||
delete this._listener[uriStr];
|
||||
}
|
||||
};
|
||||
|
||||
this._disposables.push(monaco.editor.onDidCreateModel(onModelAdd));
|
||||
this._disposables.push(monaco.editor.onWillDisposeModel(model => {
|
||||
onModelRemoved(model);
|
||||
this._resetSchema(model.uri);
|
||||
}));
|
||||
this._disposables.push(monaco.editor.onDidChangeModelLanguage(event => {
|
||||
onModelRemoved(event.model);
|
||||
onModelAdd(event.model);
|
||||
this._resetSchema(event.model.uri);
|
||||
}));
|
||||
|
||||
this._disposables.push(defaults.onDidChange(_ => {
|
||||
monaco.editor.getModels().forEach(model => {
|
||||
if (model.getModeId() === this._languageId) {
|
||||
onModelRemoved(model);
|
||||
onModelAdd(model);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
this._disposables.push({
|
||||
dispose: () => {
|
||||
monaco.editor.getModels().forEach(onModelRemoved);
|
||||
for (let key in this._listener) {
|
||||
this._listener[key].dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
monaco.editor.getModels().forEach(onModelAdd);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._disposables.forEach(d => d && d.dispose());
|
||||
this._disposables = [];
|
||||
}
|
||||
|
||||
private _resetSchema(resource: Uri): void {
|
||||
this._worker().then(worker => {
|
||||
worker.resetSchema(resource.toString());
|
||||
});
|
||||
}
|
||||
|
||||
private _doValidate(resource: Uri, languageId: string): void {
|
||||
this._worker(resource).then(worker => {
|
||||
return worker.doValidation(resource.toString()).then(diagnostics => {
|
||||
const markers = diagnostics.map(d => toDiagnostics(resource, d));
|
||||
let model = monaco.editor.getModel(resource);
|
||||
if (model.getModeId() === languageId) {
|
||||
monaco.editor.setModelMarkers(model, languageId, markers);
|
||||
}
|
||||
});
|
||||
}).then(undefined, err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function toSeverity(lsSeverity: number): monaco.MarkerSeverity {
|
||||
switch (lsSeverity) {
|
||||
case ls.DiagnosticSeverity.Error: return monaco.MarkerSeverity.Error;
|
||||
case ls.DiagnosticSeverity.Warning: return monaco.MarkerSeverity.Warning;
|
||||
case ls.DiagnosticSeverity.Information: return monaco.MarkerSeverity.Info;
|
||||
case ls.DiagnosticSeverity.Hint: return monaco.MarkerSeverity.Hint;
|
||||
default:
|
||||
return monaco.MarkerSeverity.Info;
|
||||
}
|
||||
}
|
||||
|
||||
function toDiagnostics(resource: Uri, diag: ls.Diagnostic): monaco.editor.IMarkerData {
|
||||
let code = typeof diag.code === 'number' ? String(diag.code) : <string>diag.code;
|
||||
|
||||
return {
|
||||
severity: toSeverity(diag.severity),
|
||||
startLineNumber: diag.range.start.line + 1,
|
||||
startColumn: diag.range.start.character + 1,
|
||||
endLineNumber: diag.range.end.line + 1,
|
||||
endColumn: diag.range.end.character + 1,
|
||||
message: diag.message,
|
||||
code: code,
|
||||
source: diag.source
|
||||
};
|
||||
}
|
||||
|
||||
// --- completion ------
|
||||
|
||||
function fromPosition(position: Position): ls.Position {
|
||||
if (!position) {
|
||||
return void 0;
|
||||
}
|
||||
return { character: position.column - 1, line: position.lineNumber - 1 };
|
||||
}
|
||||
|
||||
function fromRange(range: IRange): ls.Range {
|
||||
if (!range) {
|
||||
return void 0;
|
||||
}
|
||||
return { start: { line: range.startLineNumber - 1, character: range.startColumn - 1 }, end: { line: range.endLineNumber - 1, character: range.endColumn - 1 } };
|
||||
}
|
||||
function toRange(range: ls.Range): Range {
|
||||
if (!range) {
|
||||
return void 0;
|
||||
}
|
||||
return new Range(range.start.line + 1, range.start.character + 1, range.end.line + 1, range.end.character + 1);
|
||||
}
|
||||
|
||||
function toCompletionItemKind(kind: number): monaco.languages.CompletionItemKind {
|
||||
let mItemKind = monaco.languages.CompletionItemKind;
|
||||
|
||||
switch (kind) {
|
||||
case ls.CompletionItemKind.Text: return mItemKind.Text;
|
||||
case ls.CompletionItemKind.Method: return mItemKind.Method;
|
||||
case ls.CompletionItemKind.Function: return mItemKind.Function;
|
||||
case ls.CompletionItemKind.Constructor: return mItemKind.Constructor;
|
||||
case ls.CompletionItemKind.Field: return mItemKind.Field;
|
||||
case ls.CompletionItemKind.Variable: return mItemKind.Variable;
|
||||
case ls.CompletionItemKind.Class: return mItemKind.Class;
|
||||
case ls.CompletionItemKind.Interface: return mItemKind.Interface;
|
||||
case ls.CompletionItemKind.Module: return mItemKind.Module;
|
||||
case ls.CompletionItemKind.Property: return mItemKind.Property;
|
||||
case ls.CompletionItemKind.Unit: return mItemKind.Unit;
|
||||
case ls.CompletionItemKind.Value: return mItemKind.Value;
|
||||
case ls.CompletionItemKind.Enum: return mItemKind.Enum;
|
||||
case ls.CompletionItemKind.Keyword: return mItemKind.Keyword;
|
||||
case ls.CompletionItemKind.Snippet: return mItemKind.Snippet;
|
||||
case ls.CompletionItemKind.Color: return mItemKind.Color;
|
||||
case ls.CompletionItemKind.File: return mItemKind.File;
|
||||
case ls.CompletionItemKind.Reference: return mItemKind.Reference;
|
||||
}
|
||||
return mItemKind.Property;
|
||||
}
|
||||
|
||||
function fromCompletionItemKind(kind: monaco.languages.CompletionItemKind): ls.CompletionItemKind {
|
||||
let mItemKind = monaco.languages.CompletionItemKind;
|
||||
|
||||
switch (kind) {
|
||||
case mItemKind.Text: return ls.CompletionItemKind.Text;
|
||||
case mItemKind.Method: return ls.CompletionItemKind.Method;
|
||||
case mItemKind.Function: return ls.CompletionItemKind.Function;
|
||||
case mItemKind.Constructor: return ls.CompletionItemKind.Constructor;
|
||||
case mItemKind.Field: return ls.CompletionItemKind.Field;
|
||||
case mItemKind.Variable: return ls.CompletionItemKind.Variable;
|
||||
case mItemKind.Class: return ls.CompletionItemKind.Class;
|
||||
case mItemKind.Interface: return ls.CompletionItemKind.Interface;
|
||||
case mItemKind.Module: return ls.CompletionItemKind.Module;
|
||||
case mItemKind.Property: return ls.CompletionItemKind.Property;
|
||||
case mItemKind.Unit: return ls.CompletionItemKind.Unit;
|
||||
case mItemKind.Value: return ls.CompletionItemKind.Value;
|
||||
case mItemKind.Enum: return ls.CompletionItemKind.Enum;
|
||||
case mItemKind.Keyword: return ls.CompletionItemKind.Keyword;
|
||||
case mItemKind.Snippet: return ls.CompletionItemKind.Snippet;
|
||||
case mItemKind.Color: return ls.CompletionItemKind.Color;
|
||||
case mItemKind.File: return ls.CompletionItemKind.File;
|
||||
case mItemKind.Reference: return ls.CompletionItemKind.Reference;
|
||||
}
|
||||
return ls.CompletionItemKind.Property;
|
||||
}
|
||||
|
||||
function toTextEdit(textEdit: ls.TextEdit): monaco.editor.ISingleEditOperation {
|
||||
if (!textEdit) {
|
||||
return void 0;
|
||||
}
|
||||
return {
|
||||
range: toRange(textEdit.range),
|
||||
text: textEdit.newText
|
||||
}
|
||||
}
|
||||
|
||||
export class CompletionAdapter implements monaco.languages.CompletionItemProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) {
|
||||
}
|
||||
|
||||
public get triggerCharacters(): string[] {
|
||||
return [' ', ':'];
|
||||
}
|
||||
|
||||
provideCompletionItems(model: monaco.editor.IReadOnlyModel, position: Position, context: monaco.languages.CompletionContext, token: CancellationToken): Thenable<monaco.languages.CompletionList> {
|
||||
const wordInfo = model.getWordUntilPosition(position);
|
||||
const resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => {
|
||||
return worker.doComplete(resource.toString(), fromPosition(position));
|
||||
}).then(info => {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
let items: monaco.languages.CompletionItem[] = info.items.map(entry => {
|
||||
let item: monaco.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),
|
||||
};
|
||||
if (entry.textEdit) {
|
||||
item.range = toRange(entry.textEdit.range);
|
||||
item.insertText = entry.textEdit.newText;
|
||||
}
|
||||
if (entry.additionalTextEdits) {
|
||||
item.additionalTextEdits = entry.additionalTextEdits.map(toTextEdit)
|
||||
}
|
||||
if (entry.insertTextFormat === ls.InsertTextFormat.Snippet) {
|
||||
item.insertTextRules = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
return {
|
||||
isIncomplete: info.isIncomplete,
|
||||
suggestions: items
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function isMarkupContent(thing: any): thing is ls.MarkupContent {
|
||||
return thing && typeof thing === 'object' && typeof (<ls.MarkupContent>thing).kind === 'string';
|
||||
}
|
||||
|
||||
function toMarkdownString(entry: ls.MarkupContent | ls.MarkedString): monaco.IMarkdownString {
|
||||
if (typeof entry === 'string') {
|
||||
return {
|
||||
value: entry
|
||||
};
|
||||
}
|
||||
if (isMarkupContent(entry)) {
|
||||
if (entry.kind === 'plaintext') {
|
||||
return {
|
||||
value: entry.value.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&')
|
||||
};
|
||||
}
|
||||
return {
|
||||
value: entry.value
|
||||
};
|
||||
}
|
||||
|
||||
return { value: '```' + entry.language + '\n' + entry.value + '\n```\n' };
|
||||
}
|
||||
|
||||
function toMarkedStringArray(contents: ls.MarkupContent | ls.MarkedString | ls.MarkedString[]): monaco.IMarkdownString[] {
|
||||
if (!contents) {
|
||||
return void 0;
|
||||
}
|
||||
if (Array.isArray(contents)) {
|
||||
return contents.map(toMarkdownString);
|
||||
}
|
||||
return [toMarkdownString(contents)];
|
||||
}
|
||||
|
||||
|
||||
// --- hover ------
|
||||
|
||||
export class HoverAdapter implements monaco.languages.HoverProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) {}
|
||||
|
||||
provideHover(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.Hover> {
|
||||
let resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => {
|
||||
return worker.doHover(resource.toString(), fromPosition(position));
|
||||
}).then(info => {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
return <monaco.languages.Hover>{
|
||||
range: toRange(info.range),
|
||||
contents: toMarkedStringArray(info.contents)
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- document symbols ------
|
||||
|
||||
function toSymbolKind(kind: ls.SymbolKind): monaco.languages.SymbolKind {
|
||||
let mKind = monaco.languages.SymbolKind;
|
||||
|
||||
switch (kind) {
|
||||
case ls.SymbolKind.File: return mKind.Array;
|
||||
case ls.SymbolKind.Module: return mKind.Module;
|
||||
case ls.SymbolKind.Namespace: return mKind.Namespace;
|
||||
case ls.SymbolKind.Package: return mKind.Package;
|
||||
case ls.SymbolKind.Class: return mKind.Class;
|
||||
case ls.SymbolKind.Method: return mKind.Method;
|
||||
case ls.SymbolKind.Property: return mKind.Property;
|
||||
case ls.SymbolKind.Field: return mKind.Field;
|
||||
case ls.SymbolKind.Constructor: return mKind.Constructor;
|
||||
case ls.SymbolKind.Enum: return mKind.Enum;
|
||||
case ls.SymbolKind.Interface: return mKind.Interface;
|
||||
case ls.SymbolKind.Function: return mKind.Function;
|
||||
case ls.SymbolKind.Variable: return mKind.Variable;
|
||||
case ls.SymbolKind.Constant: return mKind.Constant;
|
||||
case ls.SymbolKind.String: return mKind.String;
|
||||
case ls.SymbolKind.Number: return mKind.Number;
|
||||
case ls.SymbolKind.Boolean: return mKind.Boolean;
|
||||
case ls.SymbolKind.Array: return mKind.Array;
|
||||
}
|
||||
return mKind.Function;
|
||||
}
|
||||
|
||||
|
||||
export class DocumentSymbolAdapter implements monaco.languages.DocumentSymbolProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) {
|
||||
}
|
||||
|
||||
public provideDocumentSymbols(model: monaco.editor.IReadOnlyModel, token: CancellationToken): Thenable<monaco.languages.DocumentSymbol[]> {
|
||||
const resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => worker.findDocumentSymbols(resource.toString())).then(items => {
|
||||
if (!items) {
|
||||
return;
|
||||
}
|
||||
return items.map(item => ({
|
||||
name: item.name,
|
||||
detail: '',
|
||||
containerName: item.containerName,
|
||||
kind: toSymbolKind(item.kind),
|
||||
range: toRange(item.location.range),
|
||||
selectionRange: toRange(item.location.range)
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function fromFormattingOptions(options: monaco.languages.FormattingOptions): ls.FormattingOptions {
|
||||
return {
|
||||
tabSize: options.tabSize,
|
||||
insertSpaces: options.insertSpaces
|
||||
};
|
||||
}
|
||||
|
||||
export class DocumentFormattingEditProvider implements monaco.languages.DocumentFormattingEditProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) {
|
||||
}
|
||||
|
||||
public provideDocumentFormattingEdits(model: monaco.editor.IReadOnlyModel, options: monaco.languages.FormattingOptions, token: CancellationToken): Thenable<monaco.editor.ISingleEditOperation[]> {
|
||||
const resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => {
|
||||
return worker.format(resource.toString(), null, fromFormattingOptions(options)).then(edits => {
|
||||
if (!edits || edits.length === 0) {
|
||||
return;
|
||||
}
|
||||
return edits.map(toTextEdit);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class DocumentRangeFormattingEditProvider implements monaco.languages.DocumentRangeFormattingEditProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) {
|
||||
}
|
||||
|
||||
public provideDocumentRangeFormattingEdits(model: monaco.editor.IReadOnlyModel, range: Range, options: monaco.languages.FormattingOptions, token: CancellationToken): Thenable<monaco.editor.ISingleEditOperation[]> {
|
||||
const resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => {
|
||||
return worker.format(resource.toString(), fromRange(range), fromFormattingOptions(options)).then(edits => {
|
||||
if (!edits || edits.length === 0) {
|
||||
return;
|
||||
}
|
||||
return edits.map(toTextEdit);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { LanguageServiceDefaultsImpl } from './monaco.contribution';
|
||||
import { YAMLWorker } from './yamlWorker';
|
||||
|
||||
import * as ls from 'vscode-languageserver-types';
|
||||
|
||||
import Uri = monaco.Uri;
|
||||
import Position = monaco.Position;
|
||||
import Range = monaco.Range;
|
||||
import IRange = monaco.IRange;
|
||||
import Thenable = monaco.Thenable;
|
||||
import CancellationToken = monaco.CancellationToken;
|
||||
import IDisposable = monaco.IDisposable;
|
||||
|
||||
|
||||
export interface WorkerAccessor {
|
||||
(...more: Uri[]): Thenable<YAMLWorker>
|
||||
}
|
||||
|
||||
// --- diagnostics --- ---
|
||||
|
||||
export class DiagnosticsAdapter {
|
||||
|
||||
private _disposables: IDisposable[] = [];
|
||||
private _listener: { [uri: string]: IDisposable } = Object.create(null);
|
||||
|
||||
constructor(private _languageId: string, private _worker: WorkerAccessor, defaults: LanguageServiceDefaultsImpl) {
|
||||
const onModelAdd = (model: monaco.editor.IModel): void => {
|
||||
let modeId = model.getModeId();
|
||||
if (modeId !== this._languageId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let handle: NodeJS.Timer;
|
||||
this._listener[model.uri.toString()] = model.onDidChangeContent(() => {
|
||||
clearTimeout(handle);
|
||||
handle = setTimeout(() => this._doValidate(model.uri, modeId), 500);
|
||||
});
|
||||
|
||||
this._doValidate(model.uri, modeId);
|
||||
};
|
||||
|
||||
const onModelRemoved = (model: monaco.editor.IModel): void => {
|
||||
monaco.editor.setModelMarkers(model, this._languageId, []);
|
||||
let uriStr = model.uri.toString();
|
||||
let listener = this._listener[uriStr];
|
||||
if (listener) {
|
||||
listener.dispose();
|
||||
delete this._listener[uriStr];
|
||||
}
|
||||
};
|
||||
|
||||
this._disposables.push(monaco.editor.onDidCreateModel(onModelAdd));
|
||||
this._disposables.push(monaco.editor.onWillDisposeModel(model => {
|
||||
onModelRemoved(model);
|
||||
this._resetSchema(model.uri);
|
||||
}));
|
||||
this._disposables.push(monaco.editor.onDidChangeModelLanguage(event => {
|
||||
onModelRemoved(event.model);
|
||||
onModelAdd(event.model);
|
||||
this._resetSchema(event.model.uri);
|
||||
}));
|
||||
|
||||
this._disposables.push(defaults.onDidChange(_ => {
|
||||
monaco.editor.getModels().forEach(model => {
|
||||
if (model.getModeId() === this._languageId) {
|
||||
onModelRemoved(model);
|
||||
onModelAdd(model);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
this._disposables.push({
|
||||
dispose: () => {
|
||||
monaco.editor.getModels().forEach(onModelRemoved);
|
||||
for (let key in this._listener) {
|
||||
this._listener[key].dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
monaco.editor.getModels().forEach(onModelAdd);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._disposables.forEach(d => d && d.dispose());
|
||||
this._disposables = [];
|
||||
}
|
||||
|
||||
private _resetSchema(resource: Uri): void {
|
||||
this._worker().then(worker => {
|
||||
worker.resetSchema(resource.toString());
|
||||
});
|
||||
}
|
||||
|
||||
private _doValidate(resource: Uri, languageId: string): void {
|
||||
this._worker(resource).then(worker => {
|
||||
return worker.doValidation(resource.toString()).then(diagnostics => {
|
||||
const markers = diagnostics.map(d => toDiagnostics(resource, d));
|
||||
let model = monaco.editor.getModel(resource);
|
||||
if (model.getModeId() === languageId) {
|
||||
monaco.editor.setModelMarkers(model, languageId, markers);
|
||||
}
|
||||
});
|
||||
}).then(undefined, err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function toSeverity(lsSeverity: number): monaco.MarkerSeverity {
|
||||
switch (lsSeverity) {
|
||||
case ls.DiagnosticSeverity.Error: return monaco.MarkerSeverity.Error;
|
||||
case ls.DiagnosticSeverity.Warning: return monaco.MarkerSeverity.Warning;
|
||||
case ls.DiagnosticSeverity.Information: return monaco.MarkerSeverity.Info;
|
||||
case ls.DiagnosticSeverity.Hint: return monaco.MarkerSeverity.Hint;
|
||||
default:
|
||||
return monaco.MarkerSeverity.Info;
|
||||
}
|
||||
}
|
||||
|
||||
function toDiagnostics(resource: Uri, diag: ls.Diagnostic): monaco.editor.IMarkerData {
|
||||
let code = typeof diag.code === 'number' ? String(diag.code) : <string>diag.code;
|
||||
|
||||
return {
|
||||
severity: toSeverity(diag.severity),
|
||||
startLineNumber: diag.range.start.line + 1,
|
||||
startColumn: diag.range.start.character + 1,
|
||||
endLineNumber: diag.range.end.line + 1,
|
||||
endColumn: diag.range.end.character + 1,
|
||||
message: diag.message,
|
||||
code: code,
|
||||
source: diag.source
|
||||
};
|
||||
}
|
||||
|
||||
// --- completion ------
|
||||
|
||||
function fromPosition(position: Position): ls.Position {
|
||||
if (!position) {
|
||||
return void 0;
|
||||
}
|
||||
return { character: position.column - 1, line: position.lineNumber - 1 };
|
||||
}
|
||||
|
||||
function fromRange(range: IRange): ls.Range {
|
||||
if (!range) {
|
||||
return void 0;
|
||||
}
|
||||
return { start: { line: range.startLineNumber - 1, character: range.startColumn - 1 }, end: { line: range.endLineNumber - 1, character: range.endColumn - 1 } };
|
||||
}
|
||||
function toRange(range: ls.Range): Range {
|
||||
if (!range) {
|
||||
return void 0;
|
||||
}
|
||||
return new Range(range.start.line + 1, range.start.character + 1, range.end.line + 1, range.end.character + 1);
|
||||
}
|
||||
|
||||
function toCompletionItemKind(kind: number): monaco.languages.CompletionItemKind {
|
||||
let mItemKind = monaco.languages.CompletionItemKind;
|
||||
|
||||
switch (kind) {
|
||||
case ls.CompletionItemKind.Text: return mItemKind.Text;
|
||||
case ls.CompletionItemKind.Method: return mItemKind.Method;
|
||||
case ls.CompletionItemKind.Function: return mItemKind.Function;
|
||||
case ls.CompletionItemKind.Constructor: return mItemKind.Constructor;
|
||||
case ls.CompletionItemKind.Field: return mItemKind.Field;
|
||||
case ls.CompletionItemKind.Variable: return mItemKind.Variable;
|
||||
case ls.CompletionItemKind.Class: return mItemKind.Class;
|
||||
case ls.CompletionItemKind.Interface: return mItemKind.Interface;
|
||||
case ls.CompletionItemKind.Module: return mItemKind.Module;
|
||||
case ls.CompletionItemKind.Property: return mItemKind.Property;
|
||||
case ls.CompletionItemKind.Unit: return mItemKind.Unit;
|
||||
case ls.CompletionItemKind.Value: return mItemKind.Value;
|
||||
case ls.CompletionItemKind.Enum: return mItemKind.Enum;
|
||||
case ls.CompletionItemKind.Keyword: return mItemKind.Keyword;
|
||||
case ls.CompletionItemKind.Snippet: return mItemKind.Snippet;
|
||||
case ls.CompletionItemKind.Color: return mItemKind.Color;
|
||||
case ls.CompletionItemKind.File: return mItemKind.File;
|
||||
case ls.CompletionItemKind.Reference: return mItemKind.Reference;
|
||||
}
|
||||
return mItemKind.Property;
|
||||
}
|
||||
|
||||
function fromCompletionItemKind(kind: monaco.languages.CompletionItemKind): ls.CompletionItemKind {
|
||||
let mItemKind = monaco.languages.CompletionItemKind;
|
||||
|
||||
switch (kind) {
|
||||
case mItemKind.Text: return ls.CompletionItemKind.Text;
|
||||
case mItemKind.Method: return ls.CompletionItemKind.Method;
|
||||
case mItemKind.Function: return ls.CompletionItemKind.Function;
|
||||
case mItemKind.Constructor: return ls.CompletionItemKind.Constructor;
|
||||
case mItemKind.Field: return ls.CompletionItemKind.Field;
|
||||
case mItemKind.Variable: return ls.CompletionItemKind.Variable;
|
||||
case mItemKind.Class: return ls.CompletionItemKind.Class;
|
||||
case mItemKind.Interface: return ls.CompletionItemKind.Interface;
|
||||
case mItemKind.Module: return ls.CompletionItemKind.Module;
|
||||
case mItemKind.Property: return ls.CompletionItemKind.Property;
|
||||
case mItemKind.Unit: return ls.CompletionItemKind.Unit;
|
||||
case mItemKind.Value: return ls.CompletionItemKind.Value;
|
||||
case mItemKind.Enum: return ls.CompletionItemKind.Enum;
|
||||
case mItemKind.Keyword: return ls.CompletionItemKind.Keyword;
|
||||
case mItemKind.Snippet: return ls.CompletionItemKind.Snippet;
|
||||
case mItemKind.Color: return ls.CompletionItemKind.Color;
|
||||
case mItemKind.File: return ls.CompletionItemKind.File;
|
||||
case mItemKind.Reference: return ls.CompletionItemKind.Reference;
|
||||
}
|
||||
return ls.CompletionItemKind.Property;
|
||||
}
|
||||
|
||||
function toTextEdit(textEdit: ls.TextEdit): monaco.editor.ISingleEditOperation {
|
||||
if (!textEdit) {
|
||||
return void 0;
|
||||
}
|
||||
return {
|
||||
range: toRange(textEdit.range),
|
||||
text: textEdit.newText
|
||||
}
|
||||
}
|
||||
|
||||
export class CompletionAdapter implements monaco.languages.CompletionItemProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) {
|
||||
}
|
||||
|
||||
public get triggerCharacters(): string[] {
|
||||
return [' ', ':'];
|
||||
}
|
||||
|
||||
provideCompletionItems(model: monaco.editor.IReadOnlyModel, position: Position, context: monaco.languages.CompletionContext, token: CancellationToken): Thenable<monaco.languages.CompletionList> {
|
||||
const wordInfo = model.getWordUntilPosition(position);
|
||||
const resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => {
|
||||
return worker.doComplete(resource.toString(), fromPosition(position));
|
||||
}).then(info => {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
let items: monaco.languages.CompletionItem[] = info.items.map(entry => {
|
||||
let item: monaco.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),
|
||||
};
|
||||
if (entry.textEdit) {
|
||||
item.range = toRange(entry.textEdit.range);
|
||||
item.insertText = entry.textEdit.newText;
|
||||
}
|
||||
if (entry.additionalTextEdits) {
|
||||
item.additionalTextEdits = entry.additionalTextEdits.map(toTextEdit)
|
||||
}
|
||||
if (entry.insertTextFormat === ls.InsertTextFormat.Snippet) {
|
||||
item.insertTextRules = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
return {
|
||||
isIncomplete: info.isIncomplete,
|
||||
suggestions: items
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function isMarkupContent(thing: any): thing is ls.MarkupContent {
|
||||
return thing && typeof thing === 'object' && typeof (<ls.MarkupContent>thing).kind === 'string';
|
||||
}
|
||||
|
||||
function toMarkdownString(entry: ls.MarkupContent | ls.MarkedString): monaco.IMarkdownString {
|
||||
if (typeof entry === 'string') {
|
||||
return {
|
||||
value: entry
|
||||
};
|
||||
}
|
||||
if (isMarkupContent(entry)) {
|
||||
if (entry.kind === 'plaintext') {
|
||||
return {
|
||||
value: entry.value.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&')
|
||||
};
|
||||
}
|
||||
return {
|
||||
value: entry.value
|
||||
};
|
||||
}
|
||||
|
||||
return { value: '```' + entry.language + '\n' + entry.value + '\n```\n' };
|
||||
}
|
||||
|
||||
function toMarkedStringArray(contents: ls.MarkupContent | ls.MarkedString | ls.MarkedString[]): monaco.IMarkdownString[] {
|
||||
if (!contents) {
|
||||
return void 0;
|
||||
}
|
||||
if (Array.isArray(contents)) {
|
||||
return contents.map(toMarkdownString);
|
||||
}
|
||||
return [toMarkdownString(contents)];
|
||||
}
|
||||
|
||||
|
||||
// --- hover ------
|
||||
|
||||
export class HoverAdapter implements monaco.languages.HoverProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) { }
|
||||
|
||||
provideHover(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.Hover> {
|
||||
let resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => {
|
||||
return worker.doHover(resource.toString(), fromPosition(position));
|
||||
}).then(info => {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
return <monaco.languages.Hover>{
|
||||
range: toRange(info.range),
|
||||
contents: toMarkedStringArray(info.contents)
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- document symbols ------
|
||||
|
||||
function toSymbolKind(kind: ls.SymbolKind): monaco.languages.SymbolKind {
|
||||
let mKind = monaco.languages.SymbolKind;
|
||||
|
||||
switch (kind) {
|
||||
case ls.SymbolKind.File: return mKind.Array;
|
||||
case ls.SymbolKind.Module: return mKind.Module;
|
||||
case ls.SymbolKind.Namespace: return mKind.Namespace;
|
||||
case ls.SymbolKind.Package: return mKind.Package;
|
||||
case ls.SymbolKind.Class: return mKind.Class;
|
||||
case ls.SymbolKind.Method: return mKind.Method;
|
||||
case ls.SymbolKind.Property: return mKind.Property;
|
||||
case ls.SymbolKind.Field: return mKind.Field;
|
||||
case ls.SymbolKind.Constructor: return mKind.Constructor;
|
||||
case ls.SymbolKind.Enum: return mKind.Enum;
|
||||
case ls.SymbolKind.Interface: return mKind.Interface;
|
||||
case ls.SymbolKind.Function: return mKind.Function;
|
||||
case ls.SymbolKind.Variable: return mKind.Variable;
|
||||
case ls.SymbolKind.Constant: return mKind.Constant;
|
||||
case ls.SymbolKind.String: return mKind.String;
|
||||
case ls.SymbolKind.Number: return mKind.Number;
|
||||
case ls.SymbolKind.Boolean: return mKind.Boolean;
|
||||
case ls.SymbolKind.Array: return mKind.Array;
|
||||
}
|
||||
return mKind.Function;
|
||||
}
|
||||
|
||||
|
||||
export class DocumentSymbolAdapter implements monaco.languages.DocumentSymbolProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) {
|
||||
}
|
||||
|
||||
public provideDocumentSymbols(model: monaco.editor.IReadOnlyModel, token: CancellationToken): Thenable<monaco.languages.DocumentSymbol[]> {
|
||||
const resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => worker.findDocumentSymbols(resource.toString())).then(items => {
|
||||
if (!items) {
|
||||
return;
|
||||
}
|
||||
return items.map(item => toDocumentSymbol(item));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toDocumentSymbol(item: ls.DocumentSymbol): monaco.languages.DocumentSymbol {
|
||||
return {
|
||||
detail: '',
|
||||
range: toRange(item.range),
|
||||
name: item.name,
|
||||
kind: toSymbolKind(item.kind),
|
||||
selectionRange: toRange(item.selectionRange),
|
||||
children: item.children.map(child => toDocumentSymbol(child)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function fromFormattingOptions(options: monaco.languages.FormattingOptions): ls.FormattingOptions {
|
||||
return {
|
||||
tabSize: options.tabSize,
|
||||
insertSpaces: options.insertSpaces
|
||||
};
|
||||
}
|
||||
|
||||
export class DocumentFormattingEditProvider implements monaco.languages.DocumentFormattingEditProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) {
|
||||
}
|
||||
|
||||
public provideDocumentFormattingEdits(model: monaco.editor.IReadOnlyModel, options: monaco.languages.FormattingOptions, token: CancellationToken): Thenable<monaco.editor.ISingleEditOperation[]> {
|
||||
const resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => {
|
||||
return worker.format(resource.toString(), null, fromFormattingOptions(options)).then(edits => {
|
||||
if (!edits || edits.length === 0) {
|
||||
return;
|
||||
}
|
||||
return edits.map(toTextEdit);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class DocumentRangeFormattingEditProvider implements monaco.languages.DocumentRangeFormattingEditProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) {
|
||||
}
|
||||
|
||||
public provideDocumentRangeFormattingEdits(model: monaco.editor.IReadOnlyModel, range: Range, options: monaco.languages.FormattingOptions, token: CancellationToken): Thenable<monaco.editor.ISingleEditOperation[]> {
|
||||
const resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => {
|
||||
return worker.format(resource.toString(), fromRange(range), fromFormattingOptions(options)).then(edits => {
|
||||
if (!edits || edits.length === 0) {
|
||||
return;
|
||||
}
|
||||
return edits.map(toTextEdit);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class DocumentColorAdapter implements monaco.languages.DocumentColorProvider {
|
||||
|
||||
constructor(private _worker: WorkerAccessor) {
|
||||
}
|
||||
|
||||
public provideDocumentColors(model: monaco.editor.IReadOnlyModel, token: CancellationToken): Thenable<monaco.languages.IColorInformation[]> {
|
||||
const resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => worker.findDocumentColors(resource.toString())).then(infos => {
|
||||
if (!infos) {
|
||||
return;
|
||||
}
|
||||
return infos.map(item => ({
|
||||
color: item.color,
|
||||
range: toRange(item.range)
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
public provideColorPresentations(model: monaco.editor.IReadOnlyModel, info: monaco.languages.IColorInformation, token: CancellationToken): Thenable<monaco.languages.IColorPresentation[]> {
|
||||
const resource = model.uri;
|
||||
|
||||
return this._worker(resource).then(worker => worker.getColorPresentations(resource.toString(), info.color, fromRange(info.range))).then(presentations => {
|
||||
if (!presentations) {
|
||||
return;
|
||||
}
|
||||
return presentations.map(presentation => {
|
||||
let item: monaco.languages.IColorPresentation = {
|
||||
label: presentation.label,
|
||||
};
|
||||
if (presentation.textEdit) {
|
||||
item.textEdit = toTextEdit(presentation.textEdit)
|
||||
}
|
||||
if (presentation.additionalTextEdits) {
|
||||
item.additionalTextEdits = presentation.additionalTextEdits.map(toTextEdit)
|
||||
}
|
||||
return item;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {Thenable, MarkedString, CompletionItem} from 'vscode-json-languageservice';
|
||||
import { Thenable } from './jsonLanguageTypes';
|
||||
import { MarkedString, CompletionItem } from 'vscode-languageserver-types';
|
||||
|
||||
export interface JSONWorkerContribution {
|
||||
getInfoContribution(uri: string, location: JSONPath): Thenable<MarkedString[]>;
|
||||
|
|
@ -23,4 +24,4 @@ export interface CompletionsCollector {
|
|||
log(message: string): void;
|
||||
setAsIncomplete(): void;
|
||||
getNumberOfProposals(): number;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
258
src/languageservice/jsonLanguageTypes.ts
Normal file
258
src/languageservice/jsonLanguageTypes.ts
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { JSONWorkerContribution, JSONPath, Segment, CompletionsCollector } from './jsonContributions';
|
||||
import { JSONSchema } from './jsonSchema';
|
||||
import { Range, TextEdit, Color, ColorInformation, ColorPresentation, FoldingRange, FoldingRangeKind, MarkupKind } from 'vscode-languageserver-types';
|
||||
|
||||
export {
|
||||
Range, TextEdit, JSONSchema, JSONWorkerContribution, JSONPath, Segment, CompletionsCollector,
|
||||
Color, ColorInformation, ColorPresentation, FoldingRange, FoldingRangeKind
|
||||
};
|
||||
|
||||
/**
|
||||
* Error codes used by diagnostics
|
||||
*/
|
||||
export enum ErrorCode {
|
||||
Undefined = 0,
|
||||
EnumValueMismatch = 1,
|
||||
UnexpectedEndOfComment = 0x101,
|
||||
UnexpectedEndOfString = 0x102,
|
||||
UnexpectedEndOfNumber = 0x103,
|
||||
InvalidUnicode = 0x104,
|
||||
InvalidEscapeCharacter = 0x105,
|
||||
InvalidCharacter = 0x106,
|
||||
PropertyExpected = 0x201,
|
||||
CommaExpected = 0x202,
|
||||
ColonExpected = 0x203,
|
||||
ValueExpected = 0x204,
|
||||
CommaOrCloseBacketExpected = 0x205,
|
||||
CommaOrCloseBraceExpected = 0x206,
|
||||
TrailingComma = 0x207,
|
||||
DuplicateKey = 0x208,
|
||||
CommentNotPermitted = 0x209,
|
||||
SchemaResolveError = 0x300
|
||||
}
|
||||
|
||||
export type ASTNode = ObjectASTNode | PropertyASTNode | ArrayASTNode | StringASTNode | NumberASTNode | BooleanASTNode | NullASTNode;
|
||||
|
||||
export interface BaseASTNode {
|
||||
readonly type: 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null';
|
||||
readonly parent?: ASTNode;
|
||||
readonly offset: number;
|
||||
readonly length: number;
|
||||
readonly children?: ASTNode[];
|
||||
readonly value?: string | boolean | number | null;
|
||||
}
|
||||
export interface ObjectASTNode extends BaseASTNode {
|
||||
readonly type: 'object';
|
||||
readonly properties: PropertyASTNode[];
|
||||
readonly children: ASTNode[];
|
||||
}
|
||||
export interface PropertyASTNode extends BaseASTNode {
|
||||
readonly type: 'property';
|
||||
readonly keyNode: StringASTNode;
|
||||
readonly valueNode?: ASTNode;
|
||||
readonly colonOffset?: number;
|
||||
readonly children: ASTNode[];
|
||||
}
|
||||
export interface ArrayASTNode extends BaseASTNode {
|
||||
readonly type: 'array';
|
||||
readonly items: ASTNode[];
|
||||
readonly children: ASTNode[];
|
||||
}
|
||||
export interface StringASTNode extends BaseASTNode {
|
||||
readonly type: 'string';
|
||||
readonly value: string;
|
||||
}
|
||||
export interface NumberASTNode extends BaseASTNode {
|
||||
readonly type: 'number';
|
||||
readonly value: number;
|
||||
readonly isInteger: boolean;
|
||||
}
|
||||
export interface BooleanASTNode extends BaseASTNode {
|
||||
readonly type: 'boolean';
|
||||
readonly value: boolean;
|
||||
}
|
||||
export interface NullASTNode extends BaseASTNode {
|
||||
readonly type: 'null';
|
||||
readonly value: null;
|
||||
}
|
||||
|
||||
export interface LanguageSettings {
|
||||
/**
|
||||
* If set, the validator will return syntax and semantic errors.
|
||||
*/
|
||||
validate?: boolean;
|
||||
/**
|
||||
* Defines whether comments are allowed or not. If set to false, comments will be reported as errors.
|
||||
* DocumentLanguageSettings.allowComments will override this setting.
|
||||
*/
|
||||
allowComments?: boolean;
|
||||
|
||||
/**
|
||||
* A list of known schemas and/or associations of schemas to file names.
|
||||
*/
|
||||
schemas?: SchemaConfiguration[];
|
||||
}
|
||||
|
||||
export type SeverityLevel = 'error' | 'warning' | 'ignore';
|
||||
|
||||
export interface DocumentLanguageSettings {
|
||||
/**
|
||||
* The severity of reported comments. If not set, 'LanguageSettings.allowComments' defines wheter comments are ignored or reported as errors.
|
||||
*/
|
||||
comments?: SeverityLevel;
|
||||
|
||||
/**
|
||||
* The severity of reported trailing commas. If not set, trailing commas will be reported as errors.
|
||||
*/
|
||||
trailingCommas?: SeverityLevel;
|
||||
}
|
||||
|
||||
export interface SchemaConfiguration {
|
||||
/**
|
||||
* The URI of the schema, which is also the identifier of the schema.
|
||||
*/
|
||||
uri: string;
|
||||
/**
|
||||
* A list of file names that are associated to the schema. The '*' wildcard can be used. For example '*.schema.json', 'package.json'
|
||||
*/
|
||||
fileMatch?: string[];
|
||||
/**
|
||||
* The schema for the given URI.
|
||||
* If no schema is provided, the schema will be fetched with the schema request service (if available).
|
||||
*/
|
||||
schema?: JSONSchema;
|
||||
}
|
||||
|
||||
export interface WorkspaceContextService {
|
||||
resolveRelativePath(relativePath: string, resource: string): string;
|
||||
}
|
||||
/**
|
||||
* The schema request service is used to fetch schemas. The result should the schema file comment, or,
|
||||
* in case of an error, a displayable error string
|
||||
*/
|
||||
export interface SchemaRequestService {
|
||||
(uri: string): Thenable<string>;
|
||||
}
|
||||
|
||||
export interface PromiseConstructor {
|
||||
/**
|
||||
* Creates a new Promise.
|
||||
* @param executor A callback used to initialize the promise. This callback is passed two arguments:
|
||||
* a resolve callback used resolve the promise with a value or the result of another promise,
|
||||
* and a reject callback used to reject the promise with a provided reason or error.
|
||||
*/
|
||||
new <T>(executor: (resolve: (value?: T | Thenable<T>) => void, reject: (reason?: any) => void) => void): Thenable<T>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
||||
* resolve, or rejected when any Promise is rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
all<T>(values: Array<T | Thenable<T>>): Thenable<T[]>;
|
||||
/**
|
||||
* Creates a new rejected promise for the provided reason.
|
||||
* @param reason The reason the promise was rejected.
|
||||
* @returns A new rejected Promise.
|
||||
*/
|
||||
reject<T>(reason: any): Thenable<T>;
|
||||
|
||||
/**
|
||||
* Creates a new resolved promise for the provided value.
|
||||
* @param value A promise.
|
||||
* @returns A promise whose internal state matches the provided promise.
|
||||
*/
|
||||
resolve<T>(value: T | Thenable<T>): Thenable<T>;
|
||||
|
||||
}
|
||||
|
||||
export interface Thenable<R> {
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult>;
|
||||
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => void): Thenable<TResult>;
|
||||
}
|
||||
|
||||
export interface LanguageServiceParams {
|
||||
/**
|
||||
* The schema request service is used to fetch schemas. The result should the schema file comment, or,
|
||||
* in case of an error, a displayable error string
|
||||
*/
|
||||
schemaRequestService?: SchemaRequestService;
|
||||
/**
|
||||
* The workspace context is used to resolve relative paths for relative schema references.
|
||||
*/
|
||||
workspaceContext?: WorkspaceContextService;
|
||||
/**
|
||||
* An optional set of completion and hover participants.
|
||||
*/
|
||||
contributions?: JSONWorkerContribution[];
|
||||
/**
|
||||
* A promise constructor. If not set, the ES5 Promise will be used.
|
||||
*/
|
||||
promiseConstructor?: PromiseConstructor;
|
||||
/**
|
||||
* Describes the LSP capabilities the client supports.
|
||||
*/
|
||||
clientCapabilities?: ClientCapabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes what LSP capabilities the client supports
|
||||
*/
|
||||
export interface ClientCapabilities {
|
||||
/**
|
||||
* The text document client capabilities
|
||||
*/
|
||||
textDocument?: {
|
||||
/**
|
||||
* Capabilities specific to completions.
|
||||
*/
|
||||
completion?: {
|
||||
/**
|
||||
* The client supports the following `CompletionItem` specific
|
||||
* capabilities.
|
||||
*/
|
||||
completionItem?: {
|
||||
/**
|
||||
* Client supports the follow content formats for the documentation
|
||||
* property. The order describes the preferred format of the client.
|
||||
*/
|
||||
documentationFormat?: MarkupKind[];
|
||||
};
|
||||
|
||||
};
|
||||
/**
|
||||
* Capabilities specific to hovers.
|
||||
*/
|
||||
hover?: {
|
||||
/**
|
||||
* Client supports the follow content formats for the content
|
||||
* property. The order describes the preferred format of the client.
|
||||
*/
|
||||
contentFormat?: MarkupKind[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export namespace ClientCapabilities {
|
||||
export const LATEST: ClientCapabilities = {
|
||||
textDocument: {
|
||||
completion: {
|
||||
completionItem: {
|
||||
documentationFormat: [MarkupKind.Markdown, MarkupKind.PlainText]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,53 +1,76 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Red Hat, Inc. 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';
|
||||
|
||||
export type JSONSchemaRef = JSONSchema | boolean;
|
||||
|
||||
export interface JSONSchema {
|
||||
id?: string;
|
||||
$id?: string;
|
||||
$schema?: string;
|
||||
type?: string | string[];
|
||||
title?: string;
|
||||
default?: any;
|
||||
definitions?: JSONSchemaMap;
|
||||
definitions?: { [name: string]: JSONSchema };
|
||||
description?: string;
|
||||
properties?: JSONSchemaMap;
|
||||
patternProperties?: JSONSchemaMap;
|
||||
additionalProperties?: any;
|
||||
additionalProperties?: boolean | JSONSchemaRef;
|
||||
minProperties?: number;
|
||||
maxProperties?: number;
|
||||
dependencies?: JSONSchemaMap | string[];
|
||||
items?: any;
|
||||
dependencies?: JSONSchemaMap | { [prop: string]: string[] };
|
||||
items?: JSONSchemaRef | JSONSchemaRef[];
|
||||
minItems?: number;
|
||||
maxItems?: number;
|
||||
uniqueItems?: boolean;
|
||||
additionalItems?: boolean;
|
||||
additionalItems?: boolean | JSONSchemaRef;
|
||||
pattern?: string;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
exclusiveMinimum?: boolean;
|
||||
exclusiveMaximum?: boolean;
|
||||
exclusiveMinimum?: boolean | number;
|
||||
exclusiveMaximum?: boolean | number;
|
||||
multipleOf?: number;
|
||||
required?: string[];
|
||||
$ref?: string;
|
||||
anyOf?: JSONSchema[];
|
||||
allOf?: JSONSchema[];
|
||||
oneOf?: JSONSchema[];
|
||||
not?: JSONSchema;
|
||||
anyOf?: JSONSchemaRef[];
|
||||
allOf?: JSONSchemaRef[];
|
||||
oneOf?: JSONSchemaRef[];
|
||||
not?: JSONSchemaRef;
|
||||
enum?: any[];
|
||||
format?: string;
|
||||
|
||||
// schema draft 06
|
||||
const?: any;
|
||||
contains?: JSONSchemaRef;
|
||||
propertyNames?: JSONSchemaRef;
|
||||
examples?: any[];
|
||||
|
||||
// schema draft 07
|
||||
$comment?: string;
|
||||
if?: JSONSchemaRef;
|
||||
then?: JSONSchemaRef;
|
||||
else?: JSONSchemaRef;
|
||||
|
||||
// VSCode extensions
|
||||
|
||||
defaultSnippets?: { label?: string; description?: string; markdownDescription?: string; body?: any; bodyText?: string; }[]; // VSCode extension: body: a object that will be converted to a JSON string. bodyText: text with \t and \n
|
||||
errorMessage?: string; // VSCode extension
|
||||
patternErrorMessage?: string; // VSCode extension
|
||||
deprecationMessage?: string; // VSCode extension
|
||||
enumDescriptions?: string[]; // VSCode extension
|
||||
schemaSequence?: JSONSchema[]; // extension for multiple schemas related to multiple documents in single yaml file
|
||||
markdownEnumDescriptions?: string[]; // VSCode extension
|
||||
markdownDescription?: string; // VSCode extension
|
||||
doNotSuggest?: boolean; // VSCode extension
|
||||
allowComments?: boolean; // VSCode extension
|
||||
|
||||
schemaSequence?: JSONSchema[]; // extension for multiple schemas related to multiple documents in single yaml file
|
||||
"x-kubernetes-group-version-kind"?; //Kubernetes extension
|
||||
}
|
||||
|
||||
export interface JSONSchemaMap {
|
||||
[name: string]:JSONSchema;
|
||||
[name: string]: JSONSchemaRef;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -5,270 +5,188 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { ASTNode, ErrorCode, BooleanASTNode, NullASTNode, ArrayASTNode, NumberASTNode, ObjectASTNode, PropertyASTNode, StringASTNode, IApplicableSchema, JSONDocument } from './jsonParser';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import * as Yaml from '../../yaml-ast-parser/index'
|
||||
import { Kind } from '../../yaml-ast-parser/index'
|
||||
import { Schema, Type } from 'js-yaml';
|
||||
|
||||
import { getLineStartPositions, getPosition } from '../utils/documentPositionCalculator'
|
||||
import { getLineStartPositions } from '../utils/documentPositionCalculator'
|
||||
import { parseYamlBoolean } from './scalar-type';
|
||||
|
||||
export class SingleYAMLDocument extends JSONDocument {
|
||||
private lines;
|
||||
public root;
|
||||
public errors;
|
||||
public warnings;
|
||||
|
||||
constructor(lines: number[]) {
|
||||
super(null, []);
|
||||
this.lines = lines;
|
||||
this.root = null;
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
}
|
||||
|
||||
public getSchemas(schema, doc, node) {
|
||||
let matchingSchemas = [];
|
||||
doc.validate(schema, matchingSchemas, node.start);
|
||||
return matchingSchemas;
|
||||
}
|
||||
|
||||
public getNodeFromOffset(offset: number): ASTNode {
|
||||
return this.getNodeFromOffsetEndInclusive(offset);
|
||||
}
|
||||
|
||||
private getNodeByIndent = (lines: number[], offset: number, node: ASTNode) => {
|
||||
|
||||
const { line, column: indent } = getPosition(offset, this.lines)
|
||||
|
||||
const children = node.getChildNodes()
|
||||
|
||||
function findNode(children) {
|
||||
for (var idx = 0; idx < children.length; idx++) {
|
||||
var child = children[idx];
|
||||
|
||||
const { line: childLine, column: childCol } = getPosition(child.start, lines);
|
||||
|
||||
if (childCol > indent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const newChildren = child.getChildNodes()
|
||||
const foundNode = findNode(newChildren)
|
||||
|
||||
if (foundNode) {
|
||||
return foundNode;
|
||||
}
|
||||
|
||||
// We have the right indentation, need to return based on line
|
||||
if (childLine == line) {
|
||||
return child;
|
||||
}
|
||||
if (childLine > line) {
|
||||
// Get previous
|
||||
(idx - 1) >= 0 ? children[idx - 1] : child;
|
||||
}
|
||||
// Else continue loop to try next element
|
||||
}
|
||||
|
||||
// Special case, we found the correct
|
||||
return children[children.length - 1]
|
||||
}
|
||||
|
||||
return findNode(children) || node
|
||||
}
|
||||
}
|
||||
|
||||
import { ObjectASTNodeImpl, StringASTNodeImpl, PropertyASTNodeImpl, NullASTNodeImpl, ArrayASTNodeImpl, BooleanASTNodeImpl, NumberASTNodeImpl } from './jsonParser';
|
||||
import { ASTNode, PropertyASTNode, ErrorCode } from '../jsonLanguageTypes';
|
||||
import { SingleYAMLDocument, YAMLDocument } from '../yamlLanguageTypes';
|
||||
|
||||
function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode {
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (node.kind) {
|
||||
case Yaml.Kind.MAP: {
|
||||
const instance = <Yaml.YamlMap>node;
|
||||
switch (node.kind) {
|
||||
case Yaml.Kind.MAP: {
|
||||
const instance = <Yaml.YamlMap>node;
|
||||
|
||||
const result = new ObjectASTNode(parent, null, node.startPosition, node.endPosition)
|
||||
result.addProperty
|
||||
const result = new ObjectASTNodeImpl(parent, node.startPosition, node.endPosition - node.startPosition)
|
||||
|
||||
for (const mapping of instance.mappings) {
|
||||
result.addProperty(<PropertyASTNode>recursivelyBuildAst(result, mapping))
|
||||
}
|
||||
for (const mapping of instance.mappings) {
|
||||
result.properties.push(<PropertyASTNode>recursivelyBuildAst(result, mapping));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
case Yaml.Kind.MAPPING: {
|
||||
const instance = <Yaml.YAMLMapping>node;
|
||||
const key = instance.key;
|
||||
return result;
|
||||
}
|
||||
case Yaml.Kind.MAPPING: {
|
||||
const instance = <Yaml.YAMLMapping>node;
|
||||
const key = instance.key;
|
||||
|
||||
// Technically, this is an arbitrary node in YAML
|
||||
// I doubt we would get a better string representation by parsing it
|
||||
const keyNode = new StringASTNode(null, null, true, key.startPosition, key.endPosition);
|
||||
keyNode.value = key.value;
|
||||
const result = new PropertyASTNodeImpl(<ObjectASTNodeImpl>parent, key.startPosition, instance.endPosition - key.startPosition);
|
||||
|
||||
const result = new PropertyASTNode(parent, keyNode)
|
||||
result.end = instance.endPosition
|
||||
|
||||
const valueNode = (instance.value) ? recursivelyBuildAst(result, instance.value) : new NullASTNode(parent, key.value, instance.endPosition, instance.endPosition)
|
||||
valueNode.location = key.value
|
||||
// Technically, this is an arbitrary node in YAML
|
||||
// I doubt we would get a better string representation by parsing it
|
||||
const keyNode = new StringASTNodeImpl(result, key.startPosition, key.endPosition - key.startPosition);
|
||||
keyNode.value = key.value;
|
||||
|
||||
result.setValue(valueNode)
|
||||
const valueNode = (instance.value) ? recursivelyBuildAst(result, instance.value) : new NullASTNodeImpl(parent, instance.startPosition)
|
||||
|
||||
return result;
|
||||
}
|
||||
case Yaml.Kind.SEQ: {
|
||||
const instance = <Yaml.YAMLSequence>node;
|
||||
result.keyNode = keyNode;
|
||||
result.valueNode = valueNode;
|
||||
|
||||
const result = new ArrayASTNode(parent, null, instance.startPosition, instance.endPosition);
|
||||
return result;
|
||||
}
|
||||
case Yaml.Kind.SEQ: {
|
||||
const instance = <Yaml.YAMLSequence>node;
|
||||
|
||||
let count = 0;
|
||||
for (const item of instance.items) {
|
||||
if (item === null && count === instance.items.length - 1) {
|
||||
break;
|
||||
}
|
||||
const result = new ArrayASTNodeImpl(parent, instance.startPosition, instance.endPosition - instance.startPosition);
|
||||
|
||||
// Be aware of https://github.com/nodeca/js-yaml/issues/321
|
||||
// Cannot simply work around it here because we need to know if we are in Flow or Block
|
||||
var itemNode = (item === null) ? new NullASTNode(parent, null, instance.endPosition, instance.endPosition) : recursivelyBuildAst(result, item);
|
||||
let count = 0;
|
||||
for (const item of instance.items) {
|
||||
if (item === null && count === instance.items.length - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
itemNode.location = count++;
|
||||
result.addItem(itemNode);
|
||||
}
|
||||
// Be aware of https://github.com/nodeca/js-yaml/issues/321
|
||||
// Cannot simply work around it here because we need to know if we are in Flow or Block
|
||||
var itemNode = (item === null) ? new NullASTNodeImpl(parent, instance.startPosition, instance.endPosition - instance.startPosition) : recursivelyBuildAst(result, item);
|
||||
|
||||
return result;
|
||||
}
|
||||
case Yaml.Kind.SCALAR: {
|
||||
const instance = <Yaml.YAMLScalar>node;
|
||||
const type = Yaml.determineScalarType(instance)
|
||||
result.items.push(itemNode);
|
||||
}
|
||||
|
||||
// The name is set either by the sequence or the mapping case.
|
||||
const name = null;
|
||||
const value = instance.value;
|
||||
return result;
|
||||
}
|
||||
case Yaml.Kind.SCALAR: {
|
||||
const instance = <Yaml.YAMLScalar>node;
|
||||
const type = Yaml.determineScalarType(instance)
|
||||
|
||||
//This is a patch for redirecting values with these strings to be boolean nodes because its not supported in the parser.
|
||||
let possibleBooleanValues = ['y', 'Y', 'yes', 'Yes', 'YES', 'n', 'N', 'no', 'No', 'NO', 'on', 'On', 'ON', 'off', 'Off', 'OFF'];
|
||||
if (instance.plainScalar && possibleBooleanValues.indexOf(value.toString()) !== -1) {
|
||||
return new BooleanASTNode(parent, name, parseYamlBoolean(value), node.startPosition, node.endPosition)
|
||||
}
|
||||
// The name is set either by the sequence or the mapping case.
|
||||
const name = null;
|
||||
const value = instance.value;
|
||||
|
||||
switch (type) {
|
||||
case Yaml.ScalarType.null: {
|
||||
return new StringASTNode(parent, name, false, instance.startPosition, instance.endPosition);
|
||||
}
|
||||
case Yaml.ScalarType.bool: {
|
||||
return new BooleanASTNode(parent, name, Yaml.parseYamlBoolean(value), node.startPosition, node.endPosition)
|
||||
}
|
||||
case Yaml.ScalarType.int: {
|
||||
const result = new NumberASTNode(parent, name, node.startPosition, node.endPosition);
|
||||
result.value = Yaml.parseYamlInteger(value);
|
||||
result.isInteger = true;
|
||||
return result;
|
||||
}
|
||||
case Yaml.ScalarType.float: {
|
||||
const result = new NumberASTNode(parent, name, node.startPosition, node.endPosition);
|
||||
result.value = Yaml.parseYamlFloat(value);
|
||||
result.isInteger = false;
|
||||
return result;
|
||||
}
|
||||
case Yaml.ScalarType.string: {
|
||||
const result = new StringASTNode(parent, name, false, node.startPosition, node.endPosition);
|
||||
result.value = node.value;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
//This is a patch for redirecting values with these strings to be boolean nodes because its not supported in the parser.
|
||||
let possibleBooleanValues = ['y', 'Y', 'yes', 'Yes', 'YES', 'n', 'N', 'no', 'No', 'NO', 'on', 'On', 'ON', 'off', 'Off', 'OFF'];
|
||||
if (instance.plainScalar && possibleBooleanValues.indexOf(value.toString()) !== -1) {
|
||||
return new BooleanASTNodeImpl(parent, parseYamlBoolean(value), node.startPosition, node.endPosition - node.startPosition)
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Yaml.Kind.ANCHOR_REF: {
|
||||
const instance = (<Yaml.YAMLAnchorReference>node).value
|
||||
switch (type) {
|
||||
case Yaml.ScalarType.null: {
|
||||
return new NullASTNodeImpl(parent, instance.startPosition, instance.endPosition - instance.startPosition);
|
||||
}
|
||||
case Yaml.ScalarType.bool: {
|
||||
return new BooleanASTNodeImpl(parent, Yaml.parseYamlBoolean(value), node.startPosition, node.endPosition - node.startPosition)
|
||||
}
|
||||
case Yaml.ScalarType.int: {
|
||||
const result = new NumberASTNodeImpl(parent, node.startPosition, node.endPosition - node.startPosition);
|
||||
result.value = Yaml.parseYamlInteger(value);
|
||||
result.isInteger = true;
|
||||
return result;
|
||||
}
|
||||
case Yaml.ScalarType.float: {
|
||||
const result = new NumberASTNodeImpl(parent, node.startPosition, node.endPosition - node.startPosition);
|
||||
result.value = Yaml.parseYamlFloat(value);
|
||||
result.isInteger = false;
|
||||
return result;
|
||||
}
|
||||
case Yaml.ScalarType.string: {
|
||||
const result = new StringASTNodeImpl(parent, node.startPosition, node.endPosition - node.startPosition);
|
||||
result.value = node.value;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return recursivelyBuildAst(parent, instance) ||
|
||||
new NullASTNode(parent, null, node.startPosition, node.endPosition);
|
||||
}
|
||||
case Yaml.Kind.INCLUDE_REF: {
|
||||
const result = new StringASTNode(parent, null, false, node.startPosition, node.endPosition);
|
||||
result.value = node.value;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Yaml.Kind.ANCHOR_REF: {
|
||||
const instance = (<Yaml.YAMLAnchorReference>node).value
|
||||
|
||||
return recursivelyBuildAst(parent, instance) ||
|
||||
new NullASTNodeImpl(parent, node.startPosition, node.endPosition - node.startPosition);
|
||||
}
|
||||
case Yaml.Kind.INCLUDE_REF: {
|
||||
const result = new StringASTNodeImpl(parent, node.startPosition, node.endPosition - node.startPosition);
|
||||
result.value = node.value;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function convertError(e: Yaml.Error) {
|
||||
return { message: `${e.reason}`, location: { start: e.mark.position, end: e.mark.position + e.mark.column, code: ErrorCode.Undefined } }
|
||||
return { message: `${e.reason}`, location: { start: e.mark.position, end: e.mark.position + e.mark.column, code: ErrorCode.Undefined } }
|
||||
}
|
||||
|
||||
function createJSONDocument(yamlDoc: Yaml.YAMLNode, startPositions: number[], text: string) {
|
||||
let _doc = new SingleYAMLDocument(startPositions);
|
||||
_doc.root = recursivelyBuildAst(null, yamlDoc)
|
||||
let _doc = new SingleYAMLDocument(startPositions);
|
||||
_doc.root = recursivelyBuildAst(null, yamlDoc)
|
||||
|
||||
if (!_doc.root) {
|
||||
// TODO: When this is true, consider not pushing the other errors.
|
||||
_doc.errors.push({ message: localize('Invalid symbol', 'Expected a YAML object, array or literal'), code: ErrorCode.Undefined, location: { start: yamlDoc.startPosition, end: yamlDoc.endPosition } });
|
||||
}
|
||||
if (!_doc.root) {
|
||||
// TODO: When this is true, consider not pushing the other errors.
|
||||
_doc.errors.push({ message: localize('Invalid symbol', 'Expected a YAML object, array or literal'), code: ErrorCode.Undefined, location: { start: yamlDoc.startPosition, end: yamlDoc.endPosition } });
|
||||
}
|
||||
|
||||
const duplicateKeyReason = 'duplicate key'
|
||||
const duplicateKeyReason = 'duplicate key'
|
||||
|
||||
//Patch ontop of yaml-ast-parser to disable duplicate key message on merge key
|
||||
let isDuplicateAndNotMergeKey = function (error: Yaml.Error, yamlText: string) {
|
||||
let errorConverted = convertError(error);
|
||||
let errorStart = errorConverted.location.start;
|
||||
let errorEnd = errorConverted.location.end;
|
||||
if (error.reason === duplicateKeyReason && yamlText.substring(errorStart, errorEnd).startsWith("<<")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const errors = yamlDoc.errors.filter(e => e.reason !== duplicateKeyReason && !e.isWarning).map(e => convertError(e))
|
||||
const warnings = yamlDoc.errors.filter(e => (e.reason === duplicateKeyReason && isDuplicateAndNotMergeKey(e, text)) || e.isWarning).map(e => convertError(e))
|
||||
//Patch ontop of yaml-ast-parser to disable duplicate key message on merge key
|
||||
let isDuplicateAndNotMergeKey = function (error: Yaml.Error, yamlText: string) {
|
||||
let errorConverted = convertError(error);
|
||||
let errorStart = errorConverted.location.start;
|
||||
let errorEnd = errorConverted.location.end;
|
||||
if (error.reason === duplicateKeyReason && yamlText.substring(errorStart, errorEnd).startsWith("<<")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const errors = yamlDoc.errors.filter(e => e.reason !== duplicateKeyReason && !e.isWarning).map(e => convertError(e))
|
||||
const warnings = yamlDoc.errors.filter(e => (e.reason === duplicateKeyReason && isDuplicateAndNotMergeKey(e, text)) || e.isWarning).map(e => convertError(e))
|
||||
|
||||
errors.forEach(e => _doc.errors.push(e));
|
||||
warnings.forEach(e => _doc.warnings.push(e));
|
||||
|
||||
return _doc;
|
||||
}
|
||||
|
||||
export class YAMLDocument {
|
||||
public documents: JSONDocument[]
|
||||
private errors;
|
||||
private warnings;
|
||||
|
||||
constructor(documents: JSONDocument[]) {
|
||||
this.documents = documents;
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
}
|
||||
errors.forEach(e => _doc.errors.push(e));
|
||||
warnings.forEach(e => _doc.warnings.push(e));
|
||||
|
||||
return _doc;
|
||||
}
|
||||
|
||||
export function parse(text: string, customTags = []): YAMLDocument {
|
||||
|
||||
const startPositions = getLineStartPositions(text)
|
||||
// This is documented to return a YAMLNode even though the
|
||||
// typing only returns a YAMLDocument
|
||||
const yamlDocs = []
|
||||
const startPositions = getLineStartPositions(text)
|
||||
// This is documented to return a YAMLNode even though the
|
||||
// typing only returns a YAMLDocument
|
||||
const yamlDocs = []
|
||||
|
||||
let schemaWithAdditionalTags = Schema.create(customTags.map((tag) => {
|
||||
const typeInfo = tag.split(' ');
|
||||
return new Type(typeInfo[0], { kind: typeInfo[1] || 'scalar' });
|
||||
}));
|
||||
let schemaWithAdditionalTags = Schema.create(customTags.map((tag) => {
|
||||
const typeInfo = tag.split(' ');
|
||||
return new Type(typeInfo[0], { kind: typeInfo[1] || 'scalar' });
|
||||
}));
|
||||
|
||||
//We need compiledTypeMap to be available from schemaWithAdditionalTags before we add the new custom properties
|
||||
customTags.map((tag) => {
|
||||
const typeInfo = tag.split(' ');
|
||||
schemaWithAdditionalTags.compiledTypeMap[typeInfo[0]] = new Type(typeInfo[0], { kind: typeInfo[1] || 'scalar' });
|
||||
});
|
||||
//We need compiledTypeMap to be available from schemaWithAdditionalTags before we add the new custom properties
|
||||
customTags.map((tag) => {
|
||||
const typeInfo = tag.split(' ');
|
||||
schemaWithAdditionalTags.compiledTypeMap[typeInfo[0]] = new Type(typeInfo[0], { kind: typeInfo[1] || 'scalar' });
|
||||
});
|
||||
|
||||
let additionalOptions: Yaml.LoadOptions = {
|
||||
schema: schemaWithAdditionalTags
|
||||
}
|
||||
let additionalOptions: Yaml.LoadOptions = {
|
||||
schema: schemaWithAdditionalTags
|
||||
}
|
||||
|
||||
Yaml.loadAll(text, doc => yamlDocs.push(doc), additionalOptions);
|
||||
Yaml.loadAll(text, doc => yamlDocs.push(doc), additionalOptions);
|
||||
|
||||
return new YAMLDocument(yamlDocs.map(doc => createJSONDocument(doc, startPositions, text)));
|
||||
}
|
||||
return new YAMLDocument(yamlDocs.map(doc => createJSONDocument(doc, startPositions, text)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,68 +5,130 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as Parser from '../parser/jsonParser';
|
||||
|
||||
import { SymbolInformation, SymbolKind, TextDocument, Range, Location } from 'vscode-languageserver-types';
|
||||
import { Thenable } from "../yamlLanguageService";
|
||||
import { IJSONSchemaService } from "./jsonSchemaService";
|
||||
import { SymbolKind, TextDocument, Range, DocumentSymbol, ColorInformation, ColorPresentation, Color, TextEdit } from 'vscode-languageserver-types';
|
||||
import { ASTNode, PropertyASTNode, Thenable } from '../jsonLanguageTypes';
|
||||
import { YAMLDocument, SingleYAMLDocument } from '../yamlLanguageTypes';
|
||||
import { IJSONSchemaService } from './jsonSchemaService';
|
||||
import { colorFromHex } from '../utils/colors';
|
||||
import { getNodeValue } from '../parser/jsonParser';
|
||||
|
||||
export class YAMLDocumentSymbols {
|
||||
constructor(private schemaService: IJSONSchemaService) {
|
||||
}
|
||||
|
||||
public findDocumentSymbols(document: TextDocument, doc: Parser.JSONDocument): SymbolInformation[] {
|
||||
public findDocumentSymbols(document: TextDocument, doc: YAMLDocument): DocumentSymbol[] {
|
||||
if (!doc || doc.documents.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!doc || doc["documents"].length === 0){
|
||||
return null;
|
||||
}
|
||||
let collectOutlineEntries = (result: DocumentSymbol[], node: ASTNode): DocumentSymbol[] => {
|
||||
if (node.type === 'array') {
|
||||
node.items.forEach((node, index) => {
|
||||
if (node) {
|
||||
let range = getRange(document, node);
|
||||
let selectionRange = range;
|
||||
let name = String(index);
|
||||
let children = collectOutlineEntries([], node);
|
||||
result.push({ name, kind: this.getSymbolKind(node.type), range, selectionRange, children });
|
||||
}
|
||||
});
|
||||
} else if (node.type === 'object') {
|
||||
node.properties.forEach((property: PropertyASTNode) => {
|
||||
let valueNode = property.valueNode;
|
||||
if (valueNode) {
|
||||
let range = getRange(document, property);
|
||||
let selectionRange = getRange(document, property.keyNode);
|
||||
let name = property.keyNode.value;
|
||||
let children = collectOutlineEntries([], valueNode);
|
||||
result.push({ name, kind: this.getSymbolKind(valueNode.type), range, selectionRange, children });
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
let collectOutlineEntries = (result: SymbolInformation[], node: Parser.ASTNode, containerName: string): SymbolInformation[] => {
|
||||
if (node.type === 'array') {
|
||||
(<Parser.ArrayASTNode>node).items.forEach((node: Parser.ASTNode) => {
|
||||
collectOutlineEntries(result, node, containerName);
|
||||
});
|
||||
} else if (node.type === 'object') {
|
||||
let objectNode = <Parser.ObjectASTNode>node;
|
||||
let results = [];
|
||||
for (let yamlDoc of doc.documents) {
|
||||
if (yamlDoc.root) {
|
||||
const result = collectOutlineEntries([], yamlDoc.root);
|
||||
results = results.concat(result);
|
||||
}
|
||||
}
|
||||
|
||||
objectNode.properties.forEach((property: Parser.PropertyASTNode) => {
|
||||
let location = Location.create(document.uri, Range.create(document.positionAt(property.start), document.positionAt(property.end)));
|
||||
let valueNode = property.value;
|
||||
if (valueNode) {
|
||||
let childContainerName = containerName ? containerName + '.' + property.key.value : property.key.value;
|
||||
result.push({ name: property.key.getValue(), kind: this.getSymbolKind(valueNode.type), location: location, containerName: containerName });
|
||||
collectOutlineEntries(result, valueNode, childContainerName);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return results;
|
||||
}
|
||||
|
||||
let results = [];
|
||||
for(let yamlDoc in doc["documents"]){
|
||||
let currentYAMLDoc = doc["documents"][yamlDoc];
|
||||
if(currentYAMLDoc.root){
|
||||
let result = collectOutlineEntries([], currentYAMLDoc.root, void 0);
|
||||
results = results.concat(result);
|
||||
}
|
||||
}
|
||||
private getSymbolKind(nodeType: string): SymbolKind {
|
||||
switch (nodeType) {
|
||||
case 'object':
|
||||
return SymbolKind.Module;
|
||||
case 'string':
|
||||
return SymbolKind.String;
|
||||
case 'number':
|
||||
return SymbolKind.Number;
|
||||
case 'array':
|
||||
return SymbolKind.Array;
|
||||
case 'boolean':
|
||||
return SymbolKind.Boolean;
|
||||
default: // 'null'
|
||||
return SymbolKind.Variable;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
public findDocumentColors(document: TextDocument, doc: YAMLDocument): Thenable<ColorInformation[]> {
|
||||
if (!doc || doc.documents.length === 0) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
private getSymbolKind(nodeType: string): SymbolKind {
|
||||
switch (nodeType) {
|
||||
case 'object':
|
||||
return SymbolKind.Module;
|
||||
case 'string':
|
||||
return SymbolKind.String;
|
||||
case 'number':
|
||||
return SymbolKind.Number;
|
||||
case 'array':
|
||||
return SymbolKind.Array;
|
||||
case 'boolean':
|
||||
return SymbolKind.Boolean;
|
||||
default: // 'null'
|
||||
return SymbolKind.Variable;
|
||||
}
|
||||
}
|
||||
const _findDocumentColors = (currentDoc: SingleYAMLDocument) => {
|
||||
return this.schemaService.getSchemaForResource(document.uri, currentDoc).then(schema => {
|
||||
let result: ColorInformation[] = [];
|
||||
if (schema) {
|
||||
let matchingSchemas = currentDoc.getMatchingSchemas(schema.schema);
|
||||
let visitedNode = {};
|
||||
for (let s of matchingSchemas) {
|
||||
if (!s.inverted && s.schema && (s.schema.format === 'color' || s.schema.format === 'color-hex') && s.node && s.node.type === 'string') {
|
||||
let nodeId = String(s.node.offset);
|
||||
if (!visitedNode[nodeId]) {
|
||||
let color = colorFromHex(getNodeValue(s.node));
|
||||
if (color) {
|
||||
let range = getRange(document, s.node);
|
||||
result.push({ color, range });
|
||||
}
|
||||
visitedNode[nodeId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
return Promise.all(doc.documents.map(currentDoc => _findDocumentColors(currentDoc)))
|
||||
.then(infoArray => infoArray.reduce((acc, infos) => ([...acc, ...infos]), []));
|
||||
}
|
||||
|
||||
public getColorPresentations(document: TextDocument, doc: YAMLDocument, color: Color, range: Range): ColorPresentation[] {
|
||||
let result: ColorPresentation[] = [];
|
||||
let red256 = Math.round(color.red * 255), green256 = Math.round(color.green * 255), blue256 = Math.round(color.blue * 255);
|
||||
|
||||
function toTwoDigitHex(n: number): string {
|
||||
const r = n.toString(16);
|
||||
return r.length !== 2 ? '0' + r : r;
|
||||
}
|
||||
|
||||
let label;
|
||||
if (color.alpha === 1) {
|
||||
label = `#${toTwoDigitHex(red256)}${toTwoDigitHex(green256)}${toTwoDigitHex(blue256)}`;
|
||||
} else {
|
||||
label = `#${toTwoDigitHex(red256)}${toTwoDigitHex(green256)}${toTwoDigitHex(blue256)}${toTwoDigitHex(Math.round(color.alpha * 255))}`;
|
||||
}
|
||||
result.push({ label: label, textEdit: TextEdit.replace(range, JSON.stringify(label)) });
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function getRange(document: TextDocument, node: ASTNode) {
|
||||
return Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,36 +6,17 @@
|
|||
'use strict';
|
||||
|
||||
import * as Json from 'jsonc-parser';
|
||||
import {JSONSchema, JSONSchemaMap} from '../jsonSchema';
|
||||
import {JSONSchema, JSONSchemaMap, JSONSchemaRef} from '../jsonSchema';
|
||||
import URI from 'vscode-uri';
|
||||
import * as Strings from '../utils/strings';
|
||||
import * as Parser from '../parser/jsonParser';
|
||||
import {SchemaRequestService, WorkspaceContextService, Thenable} from '../yamlLanguageService';
|
||||
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
/**
|
||||
* getParseErrorMessage has been removed from jsonc-parser since 1.0.0
|
||||
*
|
||||
* see https://github.com/Microsoft/node-jsonc-parser/blob/42ec16f9c91582d4267a0c48199cdac283c90fc9/CHANGELOG.md
|
||||
* 1.0.0
|
||||
* remove nls dependency (remove getParseErrorMessage)
|
||||
*/
|
||||
function getParseErrorMessage(errorCode: Json.ParseErrorCode): string {
|
||||
switch (errorCode) {
|
||||
case Json.ParseErrorCode.InvalidSymbol: return localize('error.invalidSymbol', 'Invalid symbol');
|
||||
case Json.ParseErrorCode.InvalidNumberFormat: return localize('error.invalidNumberFormat', 'Invalid number format');
|
||||
case Json.ParseErrorCode.PropertyNameExpected: return localize('error.propertyNameExpected', 'Property name expected');
|
||||
case Json.ParseErrorCode.ValueExpected: return localize('error.valueExpected', 'Value expected');
|
||||
case Json.ParseErrorCode.ColonExpected: return localize('error.colonExpected', 'Colon expected');
|
||||
case Json.ParseErrorCode.CommaExpected: return localize('error.commaExpected', 'Comma expected');
|
||||
case Json.ParseErrorCode.CloseBraceExpected: return localize('error.closeBraceExpected', 'Closing brace expected');
|
||||
case Json.ParseErrorCode.CloseBracketExpected: return localize('error.closeBracketExpected', 'Closing bracket expected');
|
||||
case Json.ParseErrorCode.EndOfFileExpected: return localize('error.endOfFileExpected', 'End of file expected');
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
export declare type CustomSchemaProvider = (uri: string) => Thenable<string>;
|
||||
|
||||
export interface IJSONSchemaService {
|
||||
|
||||
|
|
@ -57,7 +38,7 @@ export interface IJSONSchemaService {
|
|||
/**
|
||||
* Looks up the appropriate schema for the given URI
|
||||
*/
|
||||
getSchemaForResource(resource: string): Thenable<ResolvedSchema>;
|
||||
getSchemaForResource(resource: string, document?: Parser.JSONDocument): Thenable<ResolvedSchema>;
|
||||
|
||||
/**
|
||||
* Returns all registered schema ids
|
||||
|
|
@ -74,8 +55,6 @@ export interface ISchemaContributions {
|
|||
schemaAssociations?: ISchemaAssociations;
|
||||
}
|
||||
|
||||
export declare type CustomSchemaProvider = (uri: string) => Thenable<string>;
|
||||
|
||||
export interface ISchemaHandle {
|
||||
/**
|
||||
* The schema id
|
||||
|
|
@ -94,39 +73,31 @@ export interface ISchemaHandle {
|
|||
}
|
||||
|
||||
|
||||
export class FilePatternAssociation {
|
||||
class FilePatternAssociation {
|
||||
|
||||
private schemas: string[];
|
||||
private combinedSchemaId: string;
|
||||
private patternRegExp: RegExp;
|
||||
private combinedSchema: ISchemaHandle;
|
||||
|
||||
constructor(pattern: string) {
|
||||
this.combinedSchemaId = 'schemaservice://combinedSchema/' + encodeURIComponent(pattern);
|
||||
try {
|
||||
this.patternRegExp = Strings.convertSimple2RegExp(pattern);
|
||||
this.patternRegExp = new RegExp(Strings.convertSimple2RegExpPattern(pattern) + '$');
|
||||
} catch (e) {
|
||||
// invalid pattern
|
||||
this.patternRegExp = null;
|
||||
}
|
||||
this.schemas = [];
|
||||
this.combinedSchema = null;
|
||||
}
|
||||
|
||||
public addSchema(id: string) {
|
||||
this.schemas.push(id);
|
||||
this.combinedSchema = null;
|
||||
}
|
||||
|
||||
public matchesPattern(fileName: string): boolean {
|
||||
return this.patternRegExp && this.patternRegExp.test(fileName);
|
||||
}
|
||||
|
||||
public getCombinedSchema(service: JSONSchemaService): ISchemaHandle {
|
||||
if (!this.combinedSchema) {
|
||||
this.combinedSchema = service.createCombinedSchema(this.combinedSchemaId, this.schemas);
|
||||
}
|
||||
return this.combinedSchema;
|
||||
public getSchemas() {
|
||||
return this.schemas;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -188,40 +159,34 @@ export class ResolvedSchema {
|
|||
}
|
||||
|
||||
public getSection(path: string[]): JSONSchema {
|
||||
return this.getSectionRecursive(path, this.schema);
|
||||
return Parser.asSchema(this.getSectionRecursive(path, this.schema));
|
||||
}
|
||||
|
||||
private getSectionRecursive(path: string[], schema: JSONSchema): JSONSchema {
|
||||
if (!schema || path.length === 0) {
|
||||
private getSectionRecursive(path: string[], schema: JSONSchemaRef): JSONSchemaRef {
|
||||
if (!schema || typeof schema === 'boolean' || path.length === 0) {
|
||||
return schema;
|
||||
}
|
||||
let next = path.shift();
|
||||
|
||||
if (schema.properties && schema.properties[next]) {
|
||||
if (schema.properties && typeof schema.properties[next]) {
|
||||
return this.getSectionRecursive(path, schema.properties[next]);
|
||||
} else if (schema.patternProperties) {
|
||||
Object.keys(schema.patternProperties).forEach((pattern) => {
|
||||
for (const pattern of Object.keys(schema.patternProperties)) {
|
||||
let regex = new RegExp(pattern);
|
||||
if (regex.test(next)) {
|
||||
return this.getSectionRecursive(path, schema.patternProperties[pattern]);
|
||||
}
|
||||
});
|
||||
} else if (schema.additionalProperties) {
|
||||
}
|
||||
} else if (typeof schema.additionalProperties === 'object') {
|
||||
return this.getSectionRecursive(path, schema.additionalProperties);
|
||||
} else if (next.match('[0-9]+')) {
|
||||
if (schema.items) {
|
||||
if (Array.isArray(schema.items)) {
|
||||
let index = parseInt(next, 10);
|
||||
if (!isNaN(index) && schema.items[index]) {
|
||||
return this.getSectionRecursive(path, schema.items[index]);
|
||||
}
|
||||
} else if (schema.items) {
|
||||
return this.getSectionRecursive(path, schema.items);
|
||||
} else if (Array.isArray(schema.items)) {
|
||||
try {
|
||||
let index = parseInt(next, 10);
|
||||
if (schema.items[index]) {
|
||||
return this.getSectionRecursive(path, schema.items[index]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +213,7 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
this.contextService = contextService;
|
||||
this.requestService = requestService;
|
||||
this.callOnDispose = [];
|
||||
this.customSchemaProvider = undefined;
|
||||
|
||||
this.contributionSchemas = {};
|
||||
this.contributionAssociations = {};
|
||||
this.schemasById = {};
|
||||
|
|
@ -257,10 +222,6 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
this.registeredSchemasIds = {};
|
||||
}
|
||||
|
||||
registerCustomSchemaProvider(customSchemaProvider: CustomSchemaProvider) {
|
||||
this.customSchemaProvider = customSchemaProvider;
|
||||
}
|
||||
|
||||
public getRegisteredSchemaIds(filter?: (scheme) => boolean): string[] {
|
||||
return Object.keys(this.registeredSchemasIds).filter(id => {
|
||||
let scheme = URI.parse(id).scheme;
|
||||
|
|
@ -268,6 +229,10 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
});
|
||||
}
|
||||
|
||||
registerCustomSchemaProvider(customSchemaProvider: CustomSchemaProvider) {
|
||||
this.customSchemaProvider = customSchemaProvider;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
while (this.callOnDispose.length > 0) {
|
||||
this.callOnDispose.pop()();
|
||||
|
|
@ -304,10 +269,10 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
this.contributionAssociations[pattern] = associations;
|
||||
|
||||
var fpa = this.getOrAddFilePatternAssociation(pattern);
|
||||
associations.forEach(schemaId => {
|
||||
for (const schemaId of associations) {
|
||||
let id = this.normalizeId(schemaId);
|
||||
fpa.addSchema(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -337,9 +302,9 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
this.registeredSchemasIds[id] = true;
|
||||
|
||||
if (filePatterns) {
|
||||
filePatterns.forEach(pattern => {
|
||||
for (const pattern of filePatterns) {
|
||||
this.getOrAddFilePatternAssociation(pattern).addSchema(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
return unresolvedSchemaContent ? this.addSchemaHandle(id, unresolvedSchemaContent) : this.getOrAddSchemaHandle(id);
|
||||
}
|
||||
|
|
@ -356,11 +321,10 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
}
|
||||
for (let pattern in this.contributionAssociations) {
|
||||
var fpa = this.getOrAddFilePatternAssociation(pattern);
|
||||
|
||||
this.contributionAssociations[pattern].forEach(schemaId => {
|
||||
for (const schemaId of this.contributionAssociations[pattern]) {
|
||||
let id = this.normalizeId(schemaId);
|
||||
fpa.addSchema(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -386,13 +350,18 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
}
|
||||
|
||||
let schemaContent: JSONSchema = {};
|
||||
let jsonErrors = [];
|
||||
let jsonErrors: Json.ParseError[] = [];
|
||||
schemaContent = Json.parse(content, jsonErrors);
|
||||
let errors = jsonErrors.length ? [localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': {1}.', toDisplayString(url), getParseErrorMessage(jsonErrors[0]))] : [];
|
||||
let errors = jsonErrors.length ? [localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': Parse error at offset {1}.', toDisplayString(url), jsonErrors[0].offset)] : [];
|
||||
return new UnresolvedSchema(schemaContent, errors);
|
||||
},
|
||||
(error: any) => {
|
||||
let errorMessage = localize('json.schema.unabletoload', 'Unable to load schema from \'{0}\': {1}', toDisplayString(url), error.toString());
|
||||
let errorMessage = error.toString();
|
||||
let errorSplit = error.toString().split('Error: ');
|
||||
if(errorSplit.length > 1) {
|
||||
// more concise error message, URL and context are attached by caller anyways
|
||||
errorMessage = errorSplit[1];
|
||||
}
|
||||
return new UnresolvedSchema(<JSONSchema>{}, [errorMessage]);
|
||||
}
|
||||
);
|
||||
|
|
@ -419,21 +388,20 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
return current;
|
||||
};
|
||||
|
||||
let resolveLink = (node: any, linkedSchema: JSONSchema, linkPath: string): void => {
|
||||
let section = findSection(linkedSchema, linkPath);
|
||||
let merge = (target: JSONSchema, sourceRoot: JSONSchema, sourceURI: string, path: string): void => {
|
||||
let section = findSection(sourceRoot, path);
|
||||
if (section) {
|
||||
for (let key in section) {
|
||||
if (section.hasOwnProperty(key) && !node.hasOwnProperty(key)) {
|
||||
node[key] = section[key];
|
||||
if (section.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
|
||||
target[key] = section[key];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resolveErrors.push(localize('json.schema.invalidref', '$ref \'{0}\' in {1} can not be resolved.', linkPath, linkedSchema.id));
|
||||
resolveErrors.push(localize('json.schema.invalidref', '$ref \'{0}\' in \'{1}\' can not be resolved.', path, sourceURI));
|
||||
}
|
||||
delete node.$ref;
|
||||
};
|
||||
|
||||
let resolveExternalLink = (node: any, uri: string, linkPath: string, parentSchemaURL: string): Thenable<any> => {
|
||||
let resolveExternalLink = (node: JSONSchema, uri: string, linkPath: string, parentSchemaURL: string): Thenable<any> => {
|
||||
if (contextService && !/^\w+:\/\/.*/.test(uri)) {
|
||||
uri = contextService.resolveRelativePath(uri, parentSchemaURL);
|
||||
}
|
||||
|
|
@ -443,13 +411,13 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
let loc = linkPath ? uri + '#' + linkPath : uri;
|
||||
resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0]));
|
||||
}
|
||||
resolveLink(node, unresolvedSchema.schema, linkPath);
|
||||
merge(node, unresolvedSchema.schema, uri, linkPath);
|
||||
return resolveRefs(node, unresolvedSchema.schema, uri);
|
||||
});
|
||||
};
|
||||
|
||||
let resolveRefs = (node: JSONSchema, parentSchema: JSONSchema, parentSchemaURL: string): Thenable<any> => {
|
||||
if (!node) {
|
||||
if (!node || typeof node !== 'object') {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
|
|
@ -458,7 +426,7 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
|
||||
let openPromises: Thenable<any>[] = [];
|
||||
|
||||
let collectEntries = (...entries: JSONSchema[]) => {
|
||||
let collectEntries = (...entries: JSONSchemaRef[]) => {
|
||||
for (let entry of entries) {
|
||||
if (typeof entry === 'object') {
|
||||
toWalk.push(entry);
|
||||
|
|
@ -470,36 +438,48 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
if (typeof map === 'object') {
|
||||
for (let key in map) {
|
||||
let entry = map[key];
|
||||
toWalk.push(entry);
|
||||
if (typeof entry === 'object') {
|
||||
toWalk.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let collectArrayEntries = (...arrays: JSONSchema[][]) => {
|
||||
let collectArrayEntries = (...arrays: JSONSchemaRef[][]) => {
|
||||
for (let array of arrays) {
|
||||
if (Array.isArray(array)) {
|
||||
toWalk.push.apply(toWalk, array);
|
||||
for (let entry of array) {
|
||||
if (typeof entry === 'object') {
|
||||
toWalk.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let handleRef = (next: JSONSchema) => {
|
||||
while (next.$ref) {
|
||||
let segments = next.$ref.split('#', 2);
|
||||
delete next.$ref;
|
||||
if (segments[0].length > 0) {
|
||||
openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentSchemaURL));
|
||||
return;
|
||||
} else {
|
||||
merge(next, parentSchema, parentSchemaURL, segments[1]); // can set next.$ref again
|
||||
}
|
||||
}
|
||||
|
||||
collectEntries(<JSONSchema>next.items, <JSONSchema>next.additionalProperties, next.not, next.contains, next.propertyNames, next.if, next.then, next.else);
|
||||
collectMapEntries(next.definitions, next.properties, next.patternProperties, <JSONSchemaMap>next.dependencies);
|
||||
collectArrayEntries(next.anyOf, next.allOf, next.oneOf, <JSONSchema[]>next.items);
|
||||
};
|
||||
|
||||
while (toWalk.length) {
|
||||
let next = toWalk.pop();
|
||||
if (seen.indexOf(next) >= 0) {
|
||||
continue;
|
||||
}
|
||||
seen.push(next);
|
||||
if (next.$ref) {
|
||||
let segments = next.$ref.split('#', 2);
|
||||
if (segments[0].length > 0) {
|
||||
openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentSchemaURL));
|
||||
continue;
|
||||
} else {
|
||||
resolveLink(next, parentSchema, segments[1]);
|
||||
}
|
||||
}
|
||||
collectEntries(next.items, next.additionalProperties, next.not);
|
||||
collectMapEntries(next.definitions, next.properties, next.patternProperties, <JSONSchemaMap>next.dependencies);
|
||||
collectArrayEntries(next.anyOf, next.allOf, next.oneOf, <JSONSchema[]>next.items, next.schemaSequence);
|
||||
handleRef(next);
|
||||
}
|
||||
return Promise.all(openPromises);
|
||||
};
|
||||
|
|
@ -507,32 +487,47 @@ export class JSONSchemaService implements IJSONSchemaService {
|
|||
return resolveRefs(schema, schema, schemaURL).then(_ => new ResolvedSchema(schema, resolveErrors));
|
||||
}
|
||||
|
||||
public getSchemaForResource(resource: string ): Thenable<ResolvedSchema> {
|
||||
const resolveSchema = () => {
|
||||
// check for matching file names, last to first
|
||||
for (let i = this.filePatternAssociations.length - 1; i >= 0; i--) {
|
||||
let entry = this.filePatternAssociations[i];
|
||||
if (entry.matchesPattern(resource)) {
|
||||
return entry.getCombinedSchema(this).getResolvedSchema();
|
||||
public getSchemaForResource(resource: string, document?: Parser.JSONDocument): Thenable<ResolvedSchema> {
|
||||
|
||||
// first use $schema if present
|
||||
if (document && document.root && document.root.type === 'object') {
|
||||
let schemaProperties = document.root.properties.filter(p => (p.keyNode.value === '$schema') && p.valueNode && p.valueNode.type === 'string');
|
||||
if (schemaProperties.length > 0) {
|
||||
let schemeId = <string>Parser.getNodeValue(schemaProperties[0].valueNode);
|
||||
if (schemeId && Strings.startsWith(schemeId, '.') && this.contextService) {
|
||||
schemeId = this.contextService.resolveRelativePath(schemeId, resource);
|
||||
}
|
||||
if (schemeId) {
|
||||
let id = this.normalizeId(schemeId);
|
||||
return this.getOrAddSchemaHandle(id).getResolvedSchema();
|
||||
}
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
if (this.customSchemaProvider) {
|
||||
return this.customSchemaProvider(resource).then(schemaUri => {
|
||||
return this.loadSchema(schemaUri).then(unsolvedSchema => this.resolveSchemaContent(unsolvedSchema, schemaUri));
|
||||
}).then(schema => schema, err => {
|
||||
return resolveSchema();
|
||||
});
|
||||
} else {
|
||||
return resolveSchema();
|
||||
}
|
||||
|
||||
let seen: { [schemaId: string]: boolean } = Object.create(null);
|
||||
let schemas: string[] = [];
|
||||
for (let entry of this.filePatternAssociations) {
|
||||
if (entry.matchesPattern(resource)) {
|
||||
for (let schemaId of entry.getSchemas()) {
|
||||
if (!seen[schemaId]) {
|
||||
schemas.push(schemaId);
|
||||
seen[schemaId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (schemas.length > 0) {
|
||||
return this.createCombinedSchema(resource, schemas).getResolvedSchema();
|
||||
}
|
||||
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
public createCombinedSchema(combinedSchemaId: string, schemaIds: string[]): ISchemaHandle {
|
||||
private createCombinedSchema(resource: string, schemaIds: string[]): ISchemaHandle {
|
||||
if (schemaIds.length === 1) {
|
||||
return this.getOrAddSchemaHandle(schemaIds[0]);
|
||||
} else {
|
||||
let combinedSchemaId = 'schemaservice://combinedSchema/' + encodeURIComponent(resource);
|
||||
let combinedSchema: JSONSchema = {
|
||||
allOf: schemaIds.map(schemaId => ({ $ref: schemaId }))
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -8,62 +8,44 @@
|
|||
|
||||
import * as Parser from '../parser/jsonParser';
|
||||
import * as SchemaService from './jsonSchemaService';
|
||||
import {JSONWorkerContribution} from '../jsonContributions';
|
||||
import {Thenable} from 'vscode-json-languageservice';
|
||||
import { JSONWorkerContribution } from '../jsonContributions';
|
||||
import { Thenable } from 'vscode-json-languageservice';
|
||||
|
||||
import {Hover, TextDocument, Position, Range, MarkedString} from 'vscode-languageserver-types';
|
||||
import { Hover, TextDocument, Position, Range, MarkedString } from 'vscode-languageserver-types';
|
||||
import { YAMLDocument } from '../yamlLanguageTypes';
|
||||
import { matchOffsetToDocument } from '../utils/arrUtils';
|
||||
import { LanguageSettings } from '../yamlLanguageService';
|
||||
|
||||
export class YAMLHover {
|
||||
|
||||
private schemaService: SchemaService.IJSONSchemaService;
|
||||
private contributions: JSONWorkerContribution[];
|
||||
private shouldHover: boolean;
|
||||
constructor(private schemaService: SchemaService.IJSONSchemaService, private contributions: JSONWorkerContribution[] = []) {
|
||||
}
|
||||
|
||||
constructor(schemaService: SchemaService.IJSONSchemaService, contributions: JSONWorkerContribution[] = []) {
|
||||
this.schemaService = schemaService;
|
||||
this.contributions = contributions;
|
||||
this.shouldHover = true;
|
||||
}
|
||||
public doHover(document: TextDocument, position: Position, doc: YAMLDocument): Thenable<Hover> {
|
||||
|
||||
public configure(languageSettings: LanguageSettings){
|
||||
if(languageSettings){
|
||||
this.shouldHover = languageSettings.hover;
|
||||
}
|
||||
}
|
||||
|
||||
public doHover(document: TextDocument, position: Position, doc): Thenable<Hover> {
|
||||
|
||||
if(!this.shouldHover || !document){
|
||||
return Promise.resolve(void 0);
|
||||
}
|
||||
|
||||
let offset = document.offsetAt(position);
|
||||
let offset = document.offsetAt(position);
|
||||
let currentDoc = matchOffsetToDocument(offset, doc);
|
||||
if(currentDoc === null){
|
||||
return Promise.resolve(void 0);
|
||||
}
|
||||
const currentDocIndex = doc.documents.indexOf(currentDoc);
|
||||
let node = currentDoc.getNodeFromOffset(offset);
|
||||
if (!node || (node.type === 'object' || node.type === 'array') && offset > node.start + 1 && offset < node.end - 1) {
|
||||
return Promise.resolve(void 0);
|
||||
if (!node || (node.type === 'object' || node.type === 'array') && offset > node.offset + 1 && offset < node.offset + node.length - 1) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
let hoverRangeNode = node;
|
||||
|
||||
// use the property description when hovering over an object key
|
||||
if (node.type === 'string') {
|
||||
let stringNode = <Parser.StringASTNode>node;
|
||||
if (stringNode.isKey) {
|
||||
let propertyNode = <Parser.PropertyASTNode>node.parent;
|
||||
node = propertyNode.value;
|
||||
let parent = node.parent;
|
||||
if (parent && parent.type === 'property' && parent.keyNode === node) {
|
||||
node = parent.valueNode;
|
||||
if (!node) {
|
||||
return Promise.resolve(void 0);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let hoverRange = Range.create(document.positionAt(hoverRangeNode.start), document.positionAt(hoverRangeNode.end));
|
||||
let hoverRange = Range.create(document.positionAt(hoverRangeNode.offset), document.positionAt(hoverRangeNode.offset + hoverRangeNode.length));
|
||||
|
||||
var createHover = (contents: MarkedString[]) => {
|
||||
let result: Hover = {
|
||||
|
|
@ -73,7 +55,7 @@ export class YAMLHover {
|
|||
return result;
|
||||
};
|
||||
|
||||
let location = node.getPath();
|
||||
let location = Parser.getNodePath(node);
|
||||
for (let i = this.contributions.length - 1; i >= 0; i--) {
|
||||
let contribution = this.contributions[i];
|
||||
let promise = contribution.getInfoContribution(document.uri, location);
|
||||
|
|
@ -82,25 +64,21 @@ export class YAMLHover {
|
|||
}
|
||||
}
|
||||
|
||||
return this.schemaService.getSchemaForResource(document.uri).then((schema) => {
|
||||
return this.schemaService.getSchemaForResource(document.uri, currentDoc).then((schema) => {
|
||||
if (schema) {
|
||||
let newSchema = schema;
|
||||
if (schema.schema && schema.schema.schemaSequence && schema.schema.schemaSequence[currentDocIndex]) {
|
||||
newSchema = new SchemaService.ResolvedSchema(schema.schema.schemaSequence[currentDocIndex]);
|
||||
}
|
||||
let matchingSchemas = currentDoc.getMatchingSchemas(newSchema.schema, node.start);
|
||||
let matchingSchemas = currentDoc.getMatchingSchemas(schema.schema, node.offset);
|
||||
|
||||
let title: string = null;
|
||||
let markdownDescription: string = null;
|
||||
let markdownEnumValueDescription = null, enumValue = null;
|
||||
matchingSchemas.every((s) => {
|
||||
matchingSchemas.forEach((s) => {
|
||||
if (s.node === node && !s.inverted && s.schema) {
|
||||
title = title || s.schema.title;
|
||||
markdownDescription = markdownDescription || s.schema["markdownDescription"] || toMarkdown(s.schema.description);
|
||||
if (s.schema.enum) {
|
||||
let idx = s.schema.enum.indexOf(node.getValue());
|
||||
if (s.schema["markdownEnumDescriptions"]) {
|
||||
markdownEnumValueDescription = s.schema["markdownEnumDescriptions"][idx];
|
||||
markdownDescription = markdownDescription || s.schema.markdownDescription || toMarkdown(s.schema.description);
|
||||
if (s.schema.enum) {
|
||||
let idx = s.schema.enum.indexOf(Parser.getNodeValue(node));
|
||||
if (s.schema.markdownEnumDescriptions) {
|
||||
markdownEnumValueDescription = s.schema.markdownEnumDescriptions[idx];
|
||||
} else if (s.schema.enumDescriptions) {
|
||||
markdownEnumValueDescription = toMarkdown(s.schema.enumDescriptions[idx]);
|
||||
}
|
||||
|
|
@ -132,15 +110,15 @@ export class YAMLHover {
|
|||
}
|
||||
return createHover([result]);
|
||||
}
|
||||
return void 0;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toMarkdown(plain: string) {
|
||||
if (plain) {
|
||||
let res = plain.replace(/([^\n\r])(\r?\n)([^\n\r])/gm, '$1\n\n$3'); // single new lines to \n\n (Markdown paragraph)
|
||||
return res.replace(/[\\`*_{}[\]()#+\-.!]/g, "\\$&"); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
if (plain) {
|
||||
let res = plain.replace(/([^\n\r])(\r?\n)([^\n\r])/gm, '$1\n\n$3'); // single new lines to \n\n (Markdown paragraph)
|
||||
return res.replace(/[\\`*_{}[\]()#+\-.!]/g, "\\$&"); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,31 +6,27 @@
|
|||
'use strict';
|
||||
|
||||
import { JSONSchemaService, ResolvedSchema } from './jsonSchemaService';
|
||||
import { DiagnosticSeverity } from 'vscode-languageserver-types';
|
||||
import { LanguageSettings} from '../yamlLanguageService';
|
||||
import { DiagnosticSeverity, TextDocument } from 'vscode-languageserver-types';
|
||||
import { LanguageSettings } from '../yamlLanguageService';
|
||||
import { YAMLDocument } from '../yamlLanguageTypes';
|
||||
|
||||
export class YAMLValidation {
|
||||
|
||||
private jsonSchemaService: JSONSchemaService;
|
||||
private validationEnabled: boolean;
|
||||
private validationEnabled: boolean;
|
||||
public constructor(private jsonSchemaService: JSONSchemaService) {
|
||||
this.validationEnabled = true;
|
||||
}
|
||||
|
||||
public constructor(jsonSchemaService) {
|
||||
this.jsonSchemaService = jsonSchemaService;
|
||||
this.validationEnabled = true;
|
||||
}
|
||||
|
||||
public configure(shouldValidate: LanguageSettings){
|
||||
if(shouldValidate){
|
||||
this.validationEnabled = shouldValidate.validate;
|
||||
public configure(raw: LanguageSettings) {
|
||||
if (raw) {
|
||||
this.validationEnabled = raw.validate;
|
||||
}
|
||||
}
|
||||
|
||||
public doValidation(textDocument, yamlDocument) {
|
||||
|
||||
if(!this.validationEnabled){
|
||||
public doValidation(textDocument: TextDocument, yamlDocument: YAMLDocument) {
|
||||
if (!this.validationEnabled) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
return this.jsonSchemaService.getSchemaForResource(textDocument.uri).then(function (schema) {
|
||||
var diagnostics = [];
|
||||
var added = {};
|
||||
|
|
@ -42,10 +38,10 @@ export class YAMLValidation {
|
|||
if (schema.schema && schema.schema.schemaSequence && schema.schema.schemaSequence[documentIndex]) {
|
||||
newSchema = new ResolvedSchema(schema.schema.schemaSequence[documentIndex]);
|
||||
}
|
||||
let diagnostics = currentDoc.getValidationProblems(newSchema.schema);
|
||||
let diagnostics = currentDoc.validate(textDocument, newSchema.schema);
|
||||
for(let diag in diagnostics){
|
||||
let curDiagnostic = diagnostics[diag];
|
||||
currentDoc.errors.push({ location: { start: curDiagnostic.location.start, end: curDiagnostic.location.end }, message: curDiagnostic.message })
|
||||
currentDoc.errors.push({ location: { start: curDiagnostic.range.start, end: curDiagnostic.range.end }, message: curDiagnostic.message })
|
||||
}
|
||||
documentIndex++;
|
||||
}
|
||||
|
|
@ -93,4 +89,4 @@ export class YAMLValidation {
|
|||
return diagnostics;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +1,71 @@
|
|||
import { SingleYAMLDocument, YAMLDocument } from '../yamlLanguageTypes';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { SingleYAMLDocument } from "../parser/yamlParser";
|
||||
|
||||
export function removeDuplicates(arr, prop) {
|
||||
var new_arr = [];
|
||||
var lookup = {};
|
||||
var new_arr = [];
|
||||
var lookup = {};
|
||||
|
||||
for (var i in arr) {
|
||||
lookup[arr[i][prop]] = arr[i];
|
||||
}
|
||||
for (var i in arr) {
|
||||
lookup[arr[i][prop]] = arr[i];
|
||||
}
|
||||
|
||||
for (i in lookup) {
|
||||
new_arr.push(lookup[i]);
|
||||
}
|
||||
for (i in lookup) {
|
||||
new_arr.push(lookup[i]);
|
||||
}
|
||||
|
||||
return new_arr;
|
||||
return new_arr;
|
||||
}
|
||||
|
||||
export function getLineOffsets(textDocString: String): number[] {
|
||||
|
||||
let lineOffsets: number[] = [];
|
||||
let text = textDocString;
|
||||
let isLineStart = true;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
if (isLineStart) {
|
||||
lineOffsets.push(i);
|
||||
isLineStart = false;
|
||||
}
|
||||
let ch = text.charAt(i);
|
||||
isLineStart = (ch === '\r' || ch === '\n');
|
||||
if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (isLineStart && text.length > 0) {
|
||||
lineOffsets.push(text.length);
|
||||
}
|
||||
|
||||
return lineOffsets;
|
||||
|
||||
let lineOffsets: number[] = [];
|
||||
let text = textDocString;
|
||||
let isLineStart = true;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
if (isLineStart) {
|
||||
lineOffsets.push(i);
|
||||
isLineStart = false;
|
||||
}
|
||||
let ch = text.charAt(i);
|
||||
isLineStart = (ch === '\r' || ch === '\n');
|
||||
if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (isLineStart && text.length > 0) {
|
||||
lineOffsets.push(text.length);
|
||||
}
|
||||
|
||||
return lineOffsets;
|
||||
}
|
||||
|
||||
export function removeDuplicatesObj(objArray){
|
||||
|
||||
let nonDuplicateSet = new Set();
|
||||
let nonDuplicateArr = [];
|
||||
for(let obj in objArray){
|
||||
export function removeDuplicatesObj(objArray) {
|
||||
|
||||
let currObj = objArray[obj];
|
||||
let stringifiedObj = JSON.stringify(currObj);
|
||||
if(!nonDuplicateSet.has(stringifiedObj)){
|
||||
nonDuplicateArr.push(currObj);
|
||||
nonDuplicateSet.add(stringifiedObj);
|
||||
}
|
||||
let nonDuplicateSet = new Set();
|
||||
let nonDuplicateArr = [];
|
||||
for (let obj in objArray) {
|
||||
|
||||
}
|
||||
let currObj = objArray[obj];
|
||||
let stringifiedObj = JSON.stringify(currObj);
|
||||
if (!nonDuplicateSet.has(stringifiedObj)) {
|
||||
nonDuplicateArr.push(currObj);
|
||||
nonDuplicateSet.add(stringifiedObj);
|
||||
}
|
||||
|
||||
return nonDuplicateArr;
|
||||
}
|
||||
|
||||
return nonDuplicateArr;
|
||||
|
||||
}
|
||||
|
||||
export function matchOffsetToDocument(offset: number, jsonDocuments): SingleYAMLDocument {
|
||||
|
||||
for(let jsonDoc in jsonDocuments.documents){
|
||||
let currJsonDoc = jsonDocuments.documents[jsonDoc];
|
||||
if(currJsonDoc.root && currJsonDoc.root.end >= offset && currJsonDoc.root.start <= offset){
|
||||
return currJsonDoc;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
export function matchOffsetToDocument(offset: number, doc: YAMLDocument): SingleYAMLDocument {
|
||||
for (let currDoc of doc.documents) {
|
||||
if (currDoc.root && (currDoc.root.length + currDoc.root.offset) >= offset && currDoc.root.offset <= offset) {
|
||||
return currDoc;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
75
src/languageservice/utils/colors.ts
Normal file
75
src/languageservice/utils/colors.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { Color } from "../jsonLanguageTypes";
|
||||
|
||||
const Digit0 = 48;
|
||||
const Digit9 = 57;
|
||||
const A = 65;
|
||||
const a = 97;
|
||||
const f = 102;
|
||||
|
||||
export function hexDigit(charCode: number) {
|
||||
if (charCode < Digit0) {
|
||||
return 0;
|
||||
}
|
||||
if (charCode <= Digit9) {
|
||||
return charCode - Digit0;
|
||||
}
|
||||
if (charCode < a) {
|
||||
charCode += (a - A);
|
||||
}
|
||||
if (charCode >= a && charCode <= f) {
|
||||
return charCode - a + 10;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function colorFromHex(text: string): Color {
|
||||
if (text[0] !== '#') {
|
||||
return null;
|
||||
}
|
||||
switch (text.length) {
|
||||
case 4:
|
||||
return {
|
||||
red: (hexDigit(text.charCodeAt(1)) * 0x11) / 255.0,
|
||||
green: (hexDigit(text.charCodeAt(2)) * 0x11) / 255.0,
|
||||
blue: (hexDigit(text.charCodeAt(3)) * 0x11) / 255.0,
|
||||
alpha: 1
|
||||
};
|
||||
case 5:
|
||||
return {
|
||||
red: (hexDigit(text.charCodeAt(1)) * 0x11) / 255.0,
|
||||
green: (hexDigit(text.charCodeAt(2)) * 0x11) / 255.0,
|
||||
blue: (hexDigit(text.charCodeAt(3)) * 0x11) / 255.0,
|
||||
alpha: (hexDigit(text.charCodeAt(4)) * 0x11) / 255.0,
|
||||
};
|
||||
case 7:
|
||||
return {
|
||||
red: (hexDigit(text.charCodeAt(1)) * 0x10 + hexDigit(text.charCodeAt(2))) / 255.0,
|
||||
green: (hexDigit(text.charCodeAt(3)) * 0x10 + hexDigit(text.charCodeAt(4))) / 255.0,
|
||||
blue: (hexDigit(text.charCodeAt(5)) * 0x10 + hexDigit(text.charCodeAt(6))) / 255.0,
|
||||
alpha: 1
|
||||
};
|
||||
case 9:
|
||||
return {
|
||||
red: (hexDigit(text.charCodeAt(1)) * 0x10 + hexDigit(text.charCodeAt(2))) / 255.0,
|
||||
green: (hexDigit(text.charCodeAt(3)) * 0x10 + hexDigit(text.charCodeAt(4))) / 255.0,
|
||||
blue: (hexDigit(text.charCodeAt(5)) * 0x10 + hexDigit(text.charCodeAt(6))) / 255.0,
|
||||
alpha: (hexDigit(text.charCodeAt(7)) * 0x10 + hexDigit(text.charCodeAt(8))) / 255.0
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function colorFrom256RGB(red: number, green: number, blue: number, alpha: number = 1.0) {
|
||||
return {
|
||||
red: red / 255.0,
|
||||
green: green / 255.0,
|
||||
blue: blue / 255.0,
|
||||
alpha
|
||||
};
|
||||
}
|
||||
44
src/languageservice/utils/json.ts
Normal file
44
src/languageservice/utils/json.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
export function stringifyObject(obj: any, indent: string, stringifyLiteral: (val: any) => string) : string {
|
||||
if (obj !== null && typeof obj === 'object') {
|
||||
let newIndent = indent + '\t';
|
||||
if (Array.isArray(obj)) {
|
||||
if (obj.length === 0) {
|
||||
return '[]';
|
||||
}
|
||||
let result = '[\n';
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
result += newIndent + stringifyObject(obj[i], newIndent, stringifyLiteral);
|
||||
if (i < obj.length - 1) {
|
||||
result += ',';
|
||||
}
|
||||
result += '\n';
|
||||
}
|
||||
result += indent + ']';
|
||||
return result;
|
||||
} else {
|
||||
let keys = Object.keys(obj);
|
||||
if (keys.length === 0) {
|
||||
return '{}';
|
||||
}
|
||||
let result = '{\n';
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
|
||||
result += newIndent + JSON.stringify(key) + ': ' + stringifyObject(obj[key], newIndent, stringifyLiteral);
|
||||
if (i < keys.length - 1) {
|
||||
result += ',';
|
||||
}
|
||||
result += '\n';
|
||||
}
|
||||
result += indent + '}';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return stringifyLiteral(obj);
|
||||
}
|
||||
|
|
@ -55,4 +55,20 @@ export function equals(one: any, other: any): boolean {
|
|||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function isNumber(val: any): val is number {
|
||||
return typeof val === 'number';
|
||||
}
|
||||
|
||||
export function isDefined(val: any): val is object {
|
||||
return typeof val !== 'undefined';
|
||||
}
|
||||
|
||||
export function isBoolean(val: any): val is boolean {
|
||||
return typeof val === 'boolean';
|
||||
}
|
||||
|
||||
export function isString(val: any): val is string {
|
||||
return typeof val === 'string';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,10 +38,14 @@ export function convertSimple2RegExp(pattern: string): RegExp {
|
|||
: convertGlobalPattern2RegExp(pattern)
|
||||
}
|
||||
|
||||
export function convertSimple2RegExpPattern(pattern: string): string {
|
||||
return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*');
|
||||
}
|
||||
|
||||
function convertGlobalPattern2RegExp(pattern: string): RegExp {
|
||||
return new RegExp(pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*') + '$');
|
||||
}
|
||||
|
||||
function convertRegexString2RegExp(pattern: string, flag: string): RegExp {
|
||||
return new RegExp(pattern, flag);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { JSONSchemaService, CustomSchemaProvider } from './services/jsonSchemaService'
|
||||
import { TextDocument, Position, CompletionList, Diagnostic, FormattingOptions } from 'vscode-languageserver-types';
|
||||
import { TextDocument, Position, CompletionList, Diagnostic, FormattingOptions, DocumentSymbol, CompletionItem, Color, ColorInformation, ColorPresentation, Range } from 'vscode-languageserver-types';
|
||||
import { JSONSchema } from './jsonSchema';
|
||||
import { YAMLDocumentSymbols } from './services/documentSymbols';
|
||||
import { YAMLCompletion } from './services/yamlCompletion';
|
||||
import { YAMLHover } from './services/yamlHover';
|
||||
import { YAMLValidation } from './services/yamlValidation';
|
||||
import { format } from './services/yamlFormatter';
|
||||
import { JSONDocument, JSONWorkerContribution } from 'vscode-json-languageservice';
|
||||
import { parse as parseYAML } from "./parser/yamlParser";
|
||||
import { parse as parseYAML } from './parser/yamlParser';
|
||||
import { JSONWorkerContribution } from './jsonContributions';
|
||||
import { YAMLDocument } from './yamlLanguageTypes';
|
||||
|
||||
export interface LanguageSettings {
|
||||
validate?: boolean; //Setting for whether we want to validate the schema
|
||||
|
|
@ -24,54 +25,54 @@ export interface LanguageSettings {
|
|||
customTags?: Array<String>; //Array of Custom Tags
|
||||
}
|
||||
|
||||
export type YAMLDocument = { documents: JSONDocument[] };
|
||||
|
||||
export interface Thenable<R> {
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult>;
|
||||
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => void): Thenable<TResult>;
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult>;
|
||||
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => void): Thenable<TResult>;
|
||||
}
|
||||
|
||||
export interface WorkspaceContextService {
|
||||
resolveRelativePath(relativePath: string, resource: string): string;
|
||||
resolveRelativePath(relativePath: string, resource: string): string;
|
||||
}
|
||||
/**
|
||||
* The schema request service is used to fetch schemas. The result should the schema file comment, or,
|
||||
* in case of an error, a displayable error string
|
||||
*/
|
||||
export interface SchemaRequestService {
|
||||
(uri: string): Thenable<string>;
|
||||
(uri: string): Thenable<string>;
|
||||
}
|
||||
|
||||
export interface SchemaConfiguration {
|
||||
/**
|
||||
* The URI of the schema, which is also the identifier of the schema.
|
||||
*/
|
||||
uri: string;
|
||||
uri: string;
|
||||
/**
|
||||
* A list of file names that are associated to the schema. The '*' wildcard can be used. For example '*.schema.json', 'package.json'
|
||||
*/
|
||||
fileMatch?: string[];
|
||||
fileMatch?: string[];
|
||||
/**
|
||||
* The schema for the given URI.
|
||||
* If no schema is provided, the schema will be fetched with the schema request service (if available).
|
||||
*/
|
||||
schema?: JSONSchema;
|
||||
schema?: JSONSchema;
|
||||
}
|
||||
|
||||
export interface LanguageService {
|
||||
configure(settings: LanguageSettings): void;
|
||||
registerCustomSchemaProvider(schemaProvider: CustomSchemaProvider): void; // Register a custom schema provider
|
||||
doComplete(document: TextDocument, position: Position, doc): Thenable<CompletionList>;
|
||||
doValidation(document: TextDocument, yamlDocument): Thenable<Diagnostic[]>;
|
||||
doHover(document: TextDocument, position: Position, doc);
|
||||
findDocumentSymbols(document: TextDocument, doc);
|
||||
doResolve(completionItem);
|
||||
doComplete(document: TextDocument, position: Position, doc: YAMLDocument): Thenable<CompletionList>;
|
||||
doValidation(document: TextDocument, yamlDocument: YAMLDocument): Thenable<Diagnostic[]>;
|
||||
doHover(document: TextDocument, position: Position, doc: YAMLDocument);
|
||||
findDocumentSymbols(document: TextDocument, doc: YAMLDocument): DocumentSymbol[];
|
||||
findDocumentColors(document: TextDocument, doc: YAMLDocument): Thenable<ColorInformation[]>;
|
||||
getColorPresentations(document: TextDocument, doc: YAMLDocument, color: Color, range: Range): ColorPresentation[];
|
||||
doResolve(completionItem: CompletionItem): Thenable<CompletionItem>;
|
||||
resetSchema(uri: string): boolean;
|
||||
doFormat(document: TextDocument, options: FormattingOptions, customTags: Array<String>);
|
||||
parseYAMLDocument(document: TextDocument): YAMLDocument;
|
||||
|
|
@ -82,32 +83,30 @@ export function getLanguageService(schemaRequestService: SchemaRequestService, w
|
|||
|
||||
let completer = new YAMLCompletion(schemaService, contributions);
|
||||
let hover = new YAMLHover(schemaService, contributions);
|
||||
let yamlDocumentSymbols = new YAMLDocumentSymbols();
|
||||
let yamlDocumentSymbols = new YAMLDocumentSymbols(schemaService);
|
||||
let yamlValidation = new YAMLValidation(schemaService);
|
||||
|
||||
return {
|
||||
configure: (settings) => {
|
||||
schemaService.clearExternalSchemas();
|
||||
if (settings.schemas) {
|
||||
settings.schemas.forEach(settings => {
|
||||
schemaService.registerExternalSchema(settings.uri, settings.fileMatch, settings.schema);
|
||||
});
|
||||
}
|
||||
yamlValidation.configure(settings);
|
||||
hover.configure(settings);
|
||||
let customTagsSetting = settings && settings['customTags'] ? settings['customTags'] : [];
|
||||
completer.configure(settings, customTagsSetting);
|
||||
},
|
||||
registerCustomSchemaProvider: (schemaProvider: CustomSchemaProvider) => {
|
||||
schemaService.registerCustomSchemaProvider(schemaProvider);
|
||||
},
|
||||
doComplete: completer.doComplete.bind(completer),
|
||||
doResolve: completer.doResolve.bind(completer),
|
||||
doValidation: yamlValidation.doValidation.bind(yamlValidation),
|
||||
doHover: hover.doHover.bind(hover),
|
||||
findDocumentSymbols: yamlDocumentSymbols.findDocumentSymbols.bind(yamlDocumentSymbols),
|
||||
resetSchema: (uri: string) => schemaService.onResourceChange(uri),
|
||||
doFormat: format,
|
||||
parseYAMLDocument: (document: TextDocument) => parseYAML(document.getText()),
|
||||
configure: (settings) => {
|
||||
schemaService.clearExternalSchemas();
|
||||
if (settings.schemas) {
|
||||
settings.schemas.forEach(settings => {
|
||||
schemaService.registerExternalSchema(settings.uri, settings.fileMatch, settings.schema);
|
||||
});
|
||||
}
|
||||
},
|
||||
registerCustomSchemaProvider: (schemaProvider: CustomSchemaProvider) => {
|
||||
schemaService.registerCustomSchemaProvider(schemaProvider);
|
||||
},
|
||||
doComplete: completer.doComplete.bind(completer),
|
||||
doResolve: completer.doResolve.bind(completer),
|
||||
doValidation: yamlValidation.doValidation.bind(yamlValidation),
|
||||
doHover: hover.doHover.bind(hover),
|
||||
findDocumentSymbols: yamlDocumentSymbols.findDocumentSymbols.bind(yamlDocumentSymbols),
|
||||
findDocumentColors: yamlDocumentSymbols.findDocumentColors.bind(yamlDocumentSymbols),
|
||||
getColorPresentations: yamlDocumentSymbols.getColorPresentations.bind(yamlDocumentSymbols),
|
||||
resetSchema: (uri: string) => schemaService.onResourceChange(uri),
|
||||
doFormat: format,
|
||||
parseYAMLDocument: (document: TextDocument) => parseYAML(document.getText()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
37
src/languageservice/yamlLanguageTypes.ts
Normal file
37
src/languageservice/yamlLanguageTypes.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { JSONDocument } from './parser/jsonParser';
|
||||
import { ASTNode } from './jsonLanguageTypes';
|
||||
|
||||
export class SingleYAMLDocument extends JSONDocument {
|
||||
public lines;
|
||||
public errors;
|
||||
public warnings;
|
||||
|
||||
constructor(lines: number[]) {
|
||||
super(null, []);
|
||||
this.lines = lines;
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
}
|
||||
|
||||
public getSchemas(schema, doc, node) {
|
||||
let matchingSchemas = [];
|
||||
doc.validate(schema, matchingSchemas, node.start);
|
||||
return matchingSchemas;
|
||||
}
|
||||
|
||||
public getNodeFromOffset(offset: number, includeRightBound = false): ASTNode {
|
||||
return super.getNodeFromOffset(offset, includeRightBound);
|
||||
}
|
||||
}
|
||||
|
||||
export class YAMLDocument {
|
||||
public documents: SingleYAMLDocument[]
|
||||
public errors;
|
||||
public warnings;
|
||||
|
||||
constructor(documents: SingleYAMLDocument[]) {
|
||||
this.documents = documents;
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
}
|
||||
}
|
||||
|
|
@ -26,13 +26,14 @@ export function setupMode(defaults: LanguageServiceDefaultsImpl): void {
|
|||
|
||||
let languageId = defaults.languageId;
|
||||
|
||||
disposables.push(monaco.languages.registerCompletionItemProvider(languageId, new languageFeatures.CompletionAdapter(worker)));
|
||||
// TODO:
|
||||
// disposables.push(monaco.languages.registerCompletionItemProvider(languageId, new languageFeatures.CompletionAdapter(worker)));
|
||||
disposables.push(monaco.languages.registerHoverProvider(languageId, new languageFeatures.HoverAdapter(worker)));
|
||||
disposables.push(monaco.languages.registerDocumentSymbolProvider(languageId, new languageFeatures.DocumentSymbolAdapter(worker)));
|
||||
disposables.push(monaco.languages.registerDocumentSymbolProvider(languageId, new languageFeatures.DocumentSymbolAdapter(worker)));
|
||||
disposables.push(monaco.languages.registerColorProvider(languageId, new languageFeatures.DocumentColorAdapter(worker)));
|
||||
disposables.push(monaco.languages.registerDocumentFormattingEditProvider(languageId, new languageFeatures.DocumentFormattingEditProvider(worker)));
|
||||
disposables.push(monaco.languages.registerDocumentRangeFormattingEditProvider(languageId, new languageFeatures.DocumentRangeFormattingEditProvider(worker)));
|
||||
disposables.push(new languageFeatures.DiagnosticsAdapter(languageId, worker, defaults));
|
||||
// disposables.push(monaco.languages.setTokensProvider(languageId, createTokenizationSupport(true)));
|
||||
disposables.push(monaco.languages.setLanguageConfiguration(languageId, richEditConfiguration));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,11 +63,23 @@ export class YAMLWorker {
|
|||
resetSchema(uri: string): Thenable<boolean> {
|
||||
return Promise.as(this._languageService.resetSchema(uri));
|
||||
}
|
||||
findDocumentSymbols(uri: string): Thenable<ls.SymbolInformation[]> {
|
||||
findDocumentSymbols(uri: string): Thenable<ls.DocumentSymbol[]> {
|
||||
let document = this._getTextDocument(uri);
|
||||
let yamlDocument = this._languageService.parseYAMLDocument(document);
|
||||
let symbols = this._languageService.findDocumentSymbols(document, yamlDocument);
|
||||
return Promise.as(symbols);
|
||||
}
|
||||
findDocumentColors(uri: string): Thenable<ls.ColorInformation[]> {
|
||||
let document = this._getTextDocument(uri);
|
||||
let stylesheet = this._languageService.parseYAMLDocument(document);
|
||||
let colorSymbols = this._languageService.findDocumentColors(document, stylesheet);
|
||||
return Promise.as(colorSymbols);
|
||||
}
|
||||
getColorPresentations(uri: string, color: ls.Color, range: ls.Range): Thenable<ls.ColorPresentation[]> {
|
||||
let document = this._getTextDocument(uri);
|
||||
let stylesheet = this._languageService.parseYAMLDocument(document);
|
||||
let colorPresentations = this._languageService.getColorPresentations(document, stylesheet, color, range);
|
||||
return Promise.as(colorPresentations);
|
||||
}
|
||||
private _getTextDocument(uri: string): ls.TextDocument {
|
||||
let models = this._ctx.getMirrorModels();
|
||||
|
|
@ -88,4 +100,4 @@ export interface ICreateData {
|
|||
|
||||
export function create(ctx: IWorkerContext, createData: ICreateData): YAMLWorker {
|
||||
return new YAMLWorker(ctx, createData);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
180
test/index.html
180
test/index.html
|
|
@ -1,90 +1,90 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible"
|
||||
content="IE=edge" />
|
||||
<meta http-equiv="Content-Type"
|
||||
content="text/html;charset=utf-8" />
|
||||
<link rel="stylesheet"
|
||||
data-name="vs/editor/editor.main"
|
||||
href="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h2>Monaco Editor YAML test page</h2>
|
||||
<code id="path"></code>
|
||||
<div id="container"
|
||||
style="width:800px;height:600px;border:1px solid grey"></div>
|
||||
|
||||
<script>
|
||||
// Loading basic-languages to get the YAML language definition
|
||||
var paths = {
|
||||
'vs/basic-languages': '../node_modules/monaco-languages/release/dev',
|
||||
'vs/language/yaml': '../release/dev',
|
||||
'vs': '../node_modules/monaco-editor-core/dev/vs'
|
||||
}
|
||||
if (document.location.protocol === 'http:') {
|
||||
// Add support for running local http server
|
||||
let testIndex = document.location.pathname.indexOf('/test/');
|
||||
if (testIndex !== -1) {
|
||||
let prefix = document.location.pathname.substr(0, testIndex);
|
||||
paths['vs/language/yaml'] = prefix + '/release/dev';
|
||||
}
|
||||
}
|
||||
var require = {
|
||||
paths: paths
|
||||
};
|
||||
</script>
|
||||
<script src="../node_modules/monaco-editor-core/dev/vs/loader.js"></script>
|
||||
<script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.nls.js"></script>
|
||||
<script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.js"></script>
|
||||
|
||||
<script>
|
||||
require([
|
||||
'vs/basic-languages/monaco.contribution',
|
||||
'vs/language/yaml/monaco.contribution'
|
||||
], function () {
|
||||
const yaml = `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
namespace: default
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
apps.deployment: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
apps.deployment: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:alpine`;
|
||||
|
||||
var editor = monaco.editor.create(document.getElementById('container'), {
|
||||
value: yaml,
|
||||
language: 'yaml'
|
||||
});
|
||||
|
||||
monaco.languages.yaml.yamlDefaults.setDiagnosticsOptions({
|
||||
enableSchemaRequest: true,
|
||||
validate: true,
|
||||
schemas: [
|
||||
{
|
||||
uri: 'https://raw.githubusercontent.com/garethr/kubernetes-json-schema/master/master/deployment.json',
|
||||
fileMatch: ['*'],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible"
|
||||
content="IE=edge" />
|
||||
<meta http-equiv="Content-Type"
|
||||
content="text/html;charset=utf-8" />
|
||||
<link rel="stylesheet"
|
||||
data-name="vs/editor/editor.main"
|
||||
href="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h2>Monaco Editor YAML test page</h2>
|
||||
<code id="path"></code>
|
||||
<div id="container"
|
||||
style="width:800px;height:600px;border:1px solid grey"></div>
|
||||
|
||||
<script>
|
||||
// Loading basic-languages to get the YAML language definition
|
||||
var paths = {
|
||||
'vs/basic-languages': '../node_modules/monaco-languages/release/dev',
|
||||
'vs/language/yaml': '../release/dev',
|
||||
'vs': '../node_modules/monaco-editor-core/dev/vs'
|
||||
}
|
||||
if (document.location.protocol === 'http:') {
|
||||
// Add support for running local http server
|
||||
let testIndex = document.location.pathname.indexOf('/test/');
|
||||
if (testIndex !== -1) {
|
||||
let prefix = document.location.pathname.substr(0, testIndex);
|
||||
paths['vs/language/yaml'] = prefix + '/release/dev';
|
||||
}
|
||||
}
|
||||
var require = {
|
||||
paths: paths
|
||||
};
|
||||
</script>
|
||||
<script src="../node_modules/monaco-editor-core/dev/vs/loader.js"></script>
|
||||
<script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.nls.js"></script>
|
||||
<script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.js"></script>
|
||||
|
||||
<script>
|
||||
require([
|
||||
'vs/basic-languages/monaco.contribution',
|
||||
'vs/language/yaml/monaco.contribution'
|
||||
], function () {
|
||||
const yaml = `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
namespace: default
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
apps.deployment: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
apps.deployment: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:alpine`;
|
||||
|
||||
var editor = monaco.editor.create(document.getElementById('container'), {
|
||||
value: yaml,
|
||||
language: 'yaml'
|
||||
});
|
||||
|
||||
monaco.languages.yaml.yamlDefaults.setDiagnosticsOptions({
|
||||
enableSchemaRequest: true,
|
||||
validate: true,
|
||||
schemas: [
|
||||
{
|
||||
uri: 'https://raw.githubusercontent.com/garethr/kubernetes-json-schema/master/master/deployment.json',
|
||||
fileMatch: ['*'],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
|||
159
yarn.lock
159
yarn.lock
|
|
@ -14,6 +14,13 @@
|
|||
version "10.9.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.3.tgz#85f288502503ade0b3bfc049fe1777b05d0327d5"
|
||||
|
||||
agent-base@4, agent-base@^4.1.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
||||
integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
|
|
@ -39,6 +46,32 @@ concat-map@0.0.1:
|
|||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
|
||||
debug@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^3.1.0:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
es6-promise@^4.0.3:
|
||||
version "4.2.5"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054"
|
||||
integrity sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==
|
||||
|
||||
es6-promisify@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
|
||||
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
|
||||
dependencies:
|
||||
es6-promise "^4.0.3"
|
||||
|
||||
esprima@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
|
|
@ -58,6 +91,22 @@ glob@^7.0.5:
|
|||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
http-proxy-agent@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
|
||||
integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
|
||||
dependencies:
|
||||
agent-base "4"
|
||||
debug "3.1.0"
|
||||
|
||||
https-proxy-agent@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
|
||||
integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==
|
||||
dependencies:
|
||||
agent-base "^4.1.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
|
|
@ -76,7 +125,12 @@ js-yaml@^3.12.0:
|
|||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
jsonc-parser@^2.0.1, jsonc-parser@^2.0.2:
|
||||
jsonc-parser@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-1.0.3.tgz#1d53d7160e401a783dbceabaad82473f80e6ad7e"
|
||||
integrity sha512-hk/69oAeaIzchq/v3lS50PXuzn5O2ynldopMC+SWBql7J2WtdptfB9dy8Y7+Og5rPkTCpn83zTiO8FMcqlXJ/g==
|
||||
|
||||
jsonc-parser@^2.0.0-next.1, jsonc-parser@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.2.tgz#42fcf56d70852a043fadafde51ddb4a85649978d"
|
||||
|
||||
|
|
@ -102,6 +156,16 @@ monaco-plugin-helpers@^1.0.2:
|
|||
dependencies:
|
||||
typescript "^2.7.2"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
|
|
@ -112,6 +176,20 @@ path-is-absolute@^1.0.0:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
|
||||
prettier@^1.14.3:
|
||||
version "1.15.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.2.tgz#d31abe22afa4351efa14c7f8b94b58bb7452205e"
|
||||
integrity sha512-YgPLFFA0CdKL4Eg2IHtUSjzj/BWgszDHiNQAe0VAIBse34148whfdzLagRL+QiKS+YfK5ftB6X4v/MBw8yCoug==
|
||||
|
||||
request-light@^0.2.3:
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.4.tgz#3cea29c126682e6bcadf7915353322eeba01a755"
|
||||
integrity sha512-pM9Fq5jRnSb+82V7M97rp8FE9/YNeP2L9eckB4Szd7lyeclSIx02aIpPO/6e4m6Dy31+FBN/zkFMTd2HkNO3ow==
|
||||
dependencies:
|
||||
http-proxy-agent "^2.1.0"
|
||||
https-proxy-agent "^2.2.1"
|
||||
vscode-nls "^4.0.0"
|
||||
|
||||
requirejs@^2.3.5:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9"
|
||||
|
|
@ -146,27 +224,86 @@ uglify-es@^3.3.9:
|
|||
commander "~2.13.0"
|
||||
source-map "~0.6.1"
|
||||
|
||||
vscode-json-languageservice@^3.1.6:
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.1.6.tgz#272e21eb9abcefe6c1ed38be141f0a76d5ddf0cd"
|
||||
vscode-json-languageservice@3.0.12:
|
||||
version "3.0.12"
|
||||
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.0.12.tgz#85258632f2f7718028fbdfbb95b4ad009107b821"
|
||||
integrity sha512-XSgRVY/vsPqOa//ZwLD5DWx1wzTQGgeZfsOlVqFlLya10dpimSnd27kbuL45hzxh4B+MvmHZtZeWQKjSYnNF0A==
|
||||
dependencies:
|
||||
jsonc-parser "^2.0.1"
|
||||
vscode-languageserver-types "^3.12.0"
|
||||
vscode-nls "^3.2.4"
|
||||
vscode-uri "^1.0.6"
|
||||
jsonc-parser "^2.0.0-next.1"
|
||||
vscode-languageserver-types "^3.6.1"
|
||||
vscode-nls "^3.2.1"
|
||||
vscode-uri "^1.0.3"
|
||||
|
||||
vscode-languageserver-types@3.12.0, vscode-languageserver-types@^3.12.0:
|
||||
vscode-jsonrpc@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9"
|
||||
integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==
|
||||
|
||||
vscode-languageserver-protocol@^3.10.3:
|
||||
version "3.13.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595"
|
||||
integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg==
|
||||
dependencies:
|
||||
vscode-jsonrpc "^4.0.0"
|
||||
vscode-languageserver-types "3.13.0"
|
||||
|
||||
vscode-languageserver-types@3.12.0:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.12.0.tgz#f96051381b6a050b7175b37d6cb5d2f2eb64b944"
|
||||
integrity sha512-UxqnpzBToPO7Mi2tr/s5JeyPOSKSJtLB8lIdxCg9ZNdvP2cU8wS7iTDtwQKz91Ne4CUmTdf85ddR5SIZKXmMjQ==
|
||||
|
||||
vscode-nls@^3.2.4:
|
||||
vscode-languageserver-types@3.13.0, vscode-languageserver-types@^3.6.1:
|
||||
version "3.13.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4"
|
||||
integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA==
|
||||
|
||||
vscode-languageserver@^4.0.0:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.4.2.tgz#600ae9cc7a6ff1e84d93c7807840c2cb5b22821b"
|
||||
integrity sha512-61y8Raevi9EigDgg9NelvT9cUAohiEbUl1LOwQQgOCAaNX62yKny/ddi0uC+FUTm4CzsjhBu+06R+vYgfCYReA==
|
||||
dependencies:
|
||||
vscode-languageserver-protocol "^3.10.3"
|
||||
vscode-uri "^1.0.5"
|
||||
|
||||
vscode-nls@^3.2.1, vscode-nls@^3.2.2:
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.5.tgz#25520c1955108036dec607c85e00a522f247f1a4"
|
||||
|
||||
vscode-uri@^1.0.6:
|
||||
vscode-nls@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002"
|
||||
integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw==
|
||||
|
||||
vscode-uri@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.3.tgz#631bdbf716dccab0e65291a8dc25c23232085a52"
|
||||
integrity sha1-Yxvb9xbcyrDmUpGo3CXCMjIIWlI=
|
||||
|
||||
vscode-uri@^1.0.3, vscode-uri@^1.0.5:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
|
||||
yaml-ast-parser@0.0.40:
|
||||
version "0.0.40"
|
||||
resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.40.tgz#08536d4e73d322b1c9ce207ab8dd70e04d20ae6e"
|
||||
integrity sha1-CFNtTnPTIrHJziB6uN1w4E0grm4=
|
||||
|
||||
yaml-language-server@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yaml-language-server/-/yaml-language-server-0.1.0.tgz#ab3aff5c601ae33e03dc08c0960fd7c70296e635"
|
||||
integrity sha512-2sA9eMgBnvEnjcQVIxbakIkiL8PQ6xJw1yXfdiVRn8ms+dI3EuaaatoUuIPhYnwmDOeaobDwkJs1bpQvFO4nqQ==
|
||||
dependencies:
|
||||
js-yaml "^3.12.0"
|
||||
jsonc-parser "^1.0.3"
|
||||
prettier "^1.14.3"
|
||||
request-light "^0.2.3"
|
||||
vscode-json-languageservice "3.0.12"
|
||||
vscode-languageserver "^4.0.0"
|
||||
vscode-languageserver-types "^3.6.1"
|
||||
vscode-nls "^3.2.2"
|
||||
vscode-uri "1.0.3"
|
||||
yaml-ast-parser "0.0.40"
|
||||
|
|
|
|||
Loading…
Reference in a new issue