diff --git a/README.md b/README.md index 3e010d3..5c35294 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,9 @@ Load with ESM is added, but not yet tested. ## Development -* `git clone https://github.com/kpdecker/monaco-yaml` +* `git clone https://github.com/pengx17/monaco-yaml` * `cd monaco-yaml` * `yarn` -* `yarn watch` * open `$/monaco-yaml/test/index.html` in your favorite browser. A running example: @@ -40,4 +39,4 @@ Manually clone dependencies list below and update the project files accordingly: - `src/yaml-ast-parser`: https://github.com/mulesoft-labs/yaml-ast-parser/tree/master/src ## License -[MIT](https://github.com/kpdecker/monaco-yaml/blob/master/LICENSE.md) +[MIT](https://github.com/pengx17/monaco-yaml/blob/master/LICENSE.md) diff --git a/package.json b/package.json index df57d23..6e34f04 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,10 @@ "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/kpdecker/monaco-yaml" + "url": "https://github.com/pengx17/monaco-yaml" }, "bugs": { - "url": "https://github.com/kpdecker/monaco-yaml/issues" + "url": "https://github.com/pengx17/monaco-yaml/issues" }, "devDependencies": { "@types/chai": "^4.1.4", @@ -28,7 +28,7 @@ "@types/node": "^10.9.3", "js-yaml": "^3.12.0", "jsonc-parser": "^2.0.2", - "monaco-editor-core": "0.15.0", + "monaco-editor-core": "0.15.5", "monaco-languages": "1.6.0", "monaco-plugin-helpers": "^1.0.2", "requirejs": "^2.3.5", diff --git a/src/languageservice/parser/jsonParser.ts b/src/languageservice/parser/jsonParser.ts index 83dcdbb..1da620c 100644 --- a/src/languageservice/parser/jsonParser.ts +++ b/src/languageservice/parser/jsonParser.ts @@ -40,7 +40,7 @@ export abstract class ASTNodeImpl { constructor(parent: ASTNode, offset: number, length?: number) { this.offset = offset; - this.length = length; + this.length = length || 0; this.parent = parent; } @@ -290,6 +290,30 @@ export function contains(node: ASTNode, offset: number, includeRightBound = fals return offset >= node.offset && offset < (node.offset + node.length) || includeRightBound && offset === (node.offset + node.length); } +// export function contains(node: ASTNode, offset: number, includeRightBound = false): boolean { +// let flag = offset >= node.offset && offset <= (node.offset + node.length); +// if (!flag && includeRightBound) { +// if (node.parent && node.parent.children && ) +// const nextSibling = node.parent +// } +// return flag; +// } + +// export function findNodeAtOffset(node: ASTNode, offset: number, includeRightBound = false): ASTNode | undefined { +// if (contains(node, offset, includeRightBound)) { +// const children = node.children; +// if (Array.isArray(children)) { +// for (var i = 0; i < children.length && children[i].offset <= offset; i++) { +// const item = findNodeAtOffset(children[i], offset, includeRightBound); +// if (item) { +// return item; +// } +// } +// } +// return node; +// } +// } + export class JSONDocument { constructor(public root: ASTNode, public readonly syntaxErrors: Diagnostic[] = [], public readonly comments: Range[] = []) { @@ -298,6 +322,7 @@ export class JSONDocument { public getNodeFromOffset(offset: number, includeRightBound = false): ASTNode | undefined { if (this.root) { return Json.findNodeAtOffset(this.root, offset, includeRightBound); + //return findNodeAtOffset(this.root, offset, includeRightBound); } return void 0; } @@ -987,307 +1012,3 @@ function validate(node: ASTNode, schema: JSONSchema, validationResult: Validatio } } } - - -export function parse(textDocument: TextDocument, config?: JSONDocumentConfig): JSONDocument { - - let problems: Diagnostic[] = []; - let lastProblemOffset = -1; - let text = textDocument.getText(); - let scanner = Json.createScanner(text, false); - - let commentRanges: Range[] = config && config.collectComments ? [] : void 0; - - function _scanNext(): Json.SyntaxKind { - while (true) { - let token = scanner.scan(); - _checkScanError(); - switch (token) { - case Json.SyntaxKind.LineCommentTrivia: - case Json.SyntaxKind.BlockCommentTrivia: - if (Array.isArray(commentRanges)) { - commentRanges.push(Range.create(textDocument.positionAt(scanner.getTokenOffset()), textDocument.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()))); - } - break; - case Json.SyntaxKind.Trivia: - case Json.SyntaxKind.LineBreakTrivia: - break; - default: - return token; - } - } - } - - function _accept(token: Json.SyntaxKind): boolean { - if (scanner.getToken() === token) { - _scanNext(); - return true; - } - return false; - } - - function _errorAtRange(message: string, code: ErrorCode, startOffset: number, endOffset: number, severity: DiagnosticSeverity = DiagnosticSeverity.Error): void { - - if (problems.length === 0 || startOffset !== lastProblemOffset) { - let range = Range.create(textDocument.positionAt(startOffset), textDocument.positionAt(endOffset)); - problems.push(Diagnostic.create(range, message, severity, code, textDocument.languageId)); - lastProblemOffset = startOffset; - } - } - - function _error(message: string, code: ErrorCode, node: T = null, skipUntilAfter: Json.SyntaxKind[] = [], skipUntil: Json.SyntaxKind[] = []): T { - let start = scanner.getTokenOffset(); - let end = scanner.getTokenOffset() + scanner.getTokenLength(); - if (start === end && start > 0) { - start--; - while (start > 0 && /\s/.test(text.charAt(start))) { - start--; - } - end = start + 1; - } - _errorAtRange(message, code, start, end); - - if (node) { - _finalize(node, false); - } - if (skipUntilAfter.length + skipUntil.length > 0) { - let token = scanner.getToken(); - while (token !== Json.SyntaxKind.EOF) { - if (skipUntilAfter.indexOf(token) !== -1) { - _scanNext(); - break; - } else if (skipUntil.indexOf(token) !== -1) { - break; - } - token = _scanNext(); - } - } - return node; - } - - function _checkScanError(): boolean { - switch (scanner.getTokenError()) { - case Json.ScanError.InvalidUnicode: - _error(localize('InvalidUnicode', 'Invalid unicode sequence in string.'), ErrorCode.InvalidUnicode); - return true; - case Json.ScanError.InvalidEscapeCharacter: - _error(localize('InvalidEscapeCharacter', 'Invalid escape character in string.'), ErrorCode.InvalidEscapeCharacter); - return true; - case Json.ScanError.UnexpectedEndOfNumber: - _error(localize('UnexpectedEndOfNumber', 'Unexpected end of number.'), ErrorCode.UnexpectedEndOfNumber); - return true; - case Json.ScanError.UnexpectedEndOfComment: - _error(localize('UnexpectedEndOfComment', 'Unexpected end of comment.'), ErrorCode.UnexpectedEndOfComment); - return true; - case Json.ScanError.UnexpectedEndOfString: - _error(localize('UnexpectedEndOfString', 'Unexpected end of string.'), ErrorCode.UnexpectedEndOfString); - return true; - case Json.ScanError.InvalidCharacter: - _error(localize('InvalidCharacter', 'Invalid characters in string. Control characters must be escaped.'), ErrorCode.InvalidCharacter); - return true; - } - return false; - } - - function _finalize(node: T, scanNext: boolean): T { - node.length = scanner.getTokenOffset() + scanner.getTokenLength() - node.offset; - - if (scanNext) { - _scanNext(); - } - - return node; - } - - function _parseArray(parent: ASTNode): ArrayASTNode { - if (scanner.getToken() !== Json.SyntaxKind.OpenBracketToken) { - return null; - } - let node = new ArrayASTNodeImpl(parent, scanner.getTokenOffset()); - _scanNext(); // consume OpenBracketToken - - let count = 0; - let needsComma = false; - while (scanner.getToken() !== Json.SyntaxKind.CloseBracketToken && scanner.getToken() !== Json.SyntaxKind.EOF) { - if (scanner.getToken() === Json.SyntaxKind.CommaToken) { - if (!needsComma) { - _error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected); - } - let commaOffset = scanner.getTokenOffset(); - _scanNext(); // consume comma - if (scanner.getToken() === Json.SyntaxKind.CloseBracketToken) { - if (needsComma) { - _errorAtRange(localize('TrailingComma', 'Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1); - } - continue; - } - } else if (needsComma) { - _error(localize('ExpectedComma', 'Expected comma'), ErrorCode.CommaExpected); - } - let item = _parseValue(node, count++); - if (!item) { - _error(localize('PropertyExpected', 'Value expected'), ErrorCode.ValueExpected, null, [], [Json.SyntaxKind.CloseBracketToken, Json.SyntaxKind.CommaToken]); - } else { - node.items.push(item); - } - needsComma = true; - } - - if (scanner.getToken() !== Json.SyntaxKind.CloseBracketToken) { - return _error(localize('ExpectedCloseBracket', 'Expected comma or closing bracket'), ErrorCode.CommaOrCloseBacketExpected, node); - } - - return _finalize(node, true); - } - - function _parseProperty(parent: ObjectASTNode, keysSeen: { [key: string]: (PropertyASTNode | boolean) }): PropertyASTNode { - - let node = new PropertyASTNodeImpl(parent, scanner.getTokenOffset()); - let key = _parseString(node); - if (!key) { - if (scanner.getToken() === Json.SyntaxKind.Unknown) { - // give a more helpful error message - _error(localize('DoubleQuotesExpected', 'Property keys must be doublequoted'), ErrorCode.Undefined); - let keyNode = new StringASTNodeImpl(node, scanner.getTokenOffset(), scanner.getTokenLength()); - keyNode.value = scanner.getTokenValue(); - key = keyNode; - _scanNext(); // consume Unknown - } else { - return null; - } - } - node.keyNode = key; - - let seen = keysSeen[key.value]; - if (seen) { - _errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), ErrorCode.DuplicateKey, node.keyNode.offset, node.keyNode.offset + node.keyNode.length, DiagnosticSeverity.Warning); - if (typeof seen === 'object') { - _errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), ErrorCode.DuplicateKey, seen.keyNode.offset, seen.keyNode.offset + seen.keyNode.length, DiagnosticSeverity.Warning); - } - keysSeen[key.value] = true; // if the same key is duplicate again, avoid duplicate error reporting - } else { - keysSeen[key.value] = node; - } - - if (scanner.getToken() === Json.SyntaxKind.ColonToken) { - node.colonOffset = scanner.getTokenOffset(); - _scanNext(); // consume ColonToken - } else { - _error(localize('ColonExpected', 'Colon expected'), ErrorCode.ColonExpected); - if (scanner.getToken() === Json.SyntaxKind.StringLiteral && textDocument.positionAt(key.offset + key.length).line < textDocument.positionAt(scanner.getTokenOffset()).line) { - node.length = key.length; - return node; - } - } - let value = _parseValue(node, key.value); - if (!value) { - return _error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected, node, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]); - } - node.valueNode = value; - node.length = value.offset + value.length - node.offset; - return node; - } - - function _parseObject(parent: ASTNode): ObjectASTNode { - if (scanner.getToken() !== Json.SyntaxKind.OpenBraceToken) { - return null; - } - let node = new ObjectASTNodeImpl(parent, scanner.getTokenOffset()); - let keysSeen: any = Object.create(null); - _scanNext(); // consume OpenBraceToken - let needsComma = false; - - while (scanner.getToken() !== Json.SyntaxKind.CloseBraceToken && scanner.getToken() !== Json.SyntaxKind.EOF) { - if (scanner.getToken() === Json.SyntaxKind.CommaToken) { - if (!needsComma) { - _error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected); - } - let commaOffset = scanner.getTokenOffset(); - _scanNext(); // consume comma - if (scanner.getToken() === Json.SyntaxKind.CloseBraceToken) { - if (needsComma) { - _errorAtRange(localize('TrailingComma', 'Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1); - } - continue; - } - } else if (needsComma) { - _error(localize('ExpectedComma', 'Expected comma'), ErrorCode.CommaExpected); - } - let property = _parseProperty(node, keysSeen); - if (!property) { - _error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected, null, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]); - } else { - node.properties.push(property); - } - needsComma = true; - } - - if (scanner.getToken() !== Json.SyntaxKind.CloseBraceToken) { - return _error(localize('ExpectedCloseBrace', 'Expected comma or closing brace'), ErrorCode.CommaOrCloseBraceExpected, node); - } - return _finalize(node, true); - } - - function _parseString(parent: ASTNode): StringASTNode { - if (scanner.getToken() !== Json.SyntaxKind.StringLiteral) { - return null; - } - - let node = new StringASTNodeImpl(parent, scanner.getTokenOffset()); - node.value = scanner.getTokenValue(); - - return _finalize(node, true); - } - - function _parseNumber(parent: ASTNode): NumberASTNode { - if (scanner.getToken() !== Json.SyntaxKind.NumericLiteral) { - return null; - } - - let node = new NumberASTNodeImpl(parent, scanner.getTokenOffset()); - if (scanner.getTokenError() === Json.ScanError.None) { - let tokenValue = scanner.getTokenValue(); - try { - let numberValue = JSON.parse(tokenValue); - if (!isNumber(numberValue)) { - return _error(localize('InvalidNumberFormat', 'Invalid number format.'), ErrorCode.Undefined, node); - } - node.value = numberValue; - } catch (e) { - return _error(localize('InvalidNumberFormat', 'Invalid number format.'), ErrorCode.Undefined, node); - } - node.isInteger = tokenValue.indexOf('.') === -1; - } - return _finalize(node, true); - } - - function _parseLiteral(parent: ASTNode): ASTNode { - let node: ASTNodeImpl; - switch (scanner.getToken()) { - case Json.SyntaxKind.NullKeyword: - return _finalize(new NullASTNodeImpl(parent, scanner.getTokenOffset()), true); - case Json.SyntaxKind.TrueKeyword: - return _finalize(new BooleanASTNodeImpl(parent, true, scanner.getTokenOffset()), true); - case Json.SyntaxKind.FalseKeyword: - return _finalize(new BooleanASTNodeImpl(parent, false, scanner.getTokenOffset()), true); - default: - return null; - } - } - - function _parseValue(parent: ASTNode, name: Json.Segment): ASTNode { - return _parseArray(parent) || _parseObject(parent) || _parseString(parent) || _parseNumber(parent) || _parseLiteral(parent); - } - - let _root = null; - let token = _scanNext(); - if (token !== Json.SyntaxKind.EOF) { - _root = _parseValue(null, null); - if (!_root) { - _error(localize('Invalid symbol', 'Expected a JSON object, array or literal.'), ErrorCode.Undefined); - } else if (scanner.getToken() !== Json.SyntaxKind.EOF) { - _error(localize('End of file expected', 'End of file expected.'), ErrorCode.Undefined); - } - } - return new JSONDocument(_root, problems, commentRanges); -} diff --git a/src/languageservice/parser/yamlParser.ts b/src/languageservice/parser/yamlParser.ts index 1ddf5e2..083693e 100644 --- a/src/languageservice/parser/yamlParser.ts +++ b/src/languageservice/parser/yamlParser.ts @@ -9,13 +9,14 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); import * as Yaml from '../../yaml-ast-parser/index' -import { Schema, Type } from 'js-yaml'; import { getLineStartPositions } from '../utils/documentPositionCalculator' import { parseYamlBoolean } from './scalar-type'; import { ObjectASTNodeImpl, StringASTNodeImpl, PropertyASTNodeImpl, NullASTNodeImpl, ArrayASTNodeImpl, BooleanASTNodeImpl, NumberASTNodeImpl } from './jsonParser'; import { ASTNode, PropertyASTNode, ErrorCode } from '../jsonLanguageTypes'; import { SingleYAMLDocument, YAMLDocument } from '../yamlLanguageTypes'; +import { Schema } from '../../yaml-ast-parser/schema'; +import { Type } from '../../yaml-ast-parser/type'; function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode { @@ -47,7 +48,8 @@ function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode { const keyNode = new StringASTNodeImpl(result, key.startPosition, key.endPosition - key.startPosition); keyNode.value = key.value; - const valueNode = (instance.value) ? recursivelyBuildAst(result, instance.value) : new NullASTNodeImpl(parent, instance.startPosition) + // TODO: calculate the correct NULL range. + const valueNode = (instance.value) ? recursivelyBuildAst(result, instance.value) : new NullASTNodeImpl(parent, instance.endPosition); result.keyNode = keyNode; result.valueNode = valueNode; @@ -131,7 +133,7 @@ function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode { } 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 - e.mark.column, end: e.mark.position, code: ErrorCode.Undefined } } } function createJSONDocument(yamlDoc: Yaml.YAMLNode, startPositions: number[], text: string) { @@ -169,7 +171,7 @@ 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 yamlDocs: Yaml.YAMLNode[] = [] let schemaWithAdditionalTags = Schema.create(customTags.map((tag) => { const typeInfo = tag.split(' '); diff --git a/src/languageservice/services/yamlCompletion.ts b/src/languageservice/services/yamlCompletion.ts index 704c29d..e6e97d8 100644 --- a/src/languageservice/services/yamlCompletion.ts +++ b/src/languageservice/services/yamlCompletion.ts @@ -58,11 +58,13 @@ export class YAMLCompletion { let currentDoc = matchOffsetToDocument(offset, doc); - if(currentDoc === null){ - return Promise.resolve(result); - } + const currentDocIndex = doc.documents.indexOf(currentDoc); - let node = currentDoc.getNodeFromOffset(offset, true); + if (currentDoc === null) { + return Promise.resolve(result); + } + + let node = currentDoc.getNodeFromOffsetEndInclusive(offset); if (this.isInComment(document, node ? node.offset : 0, offset)) { return Promise.resolve(result); } @@ -131,12 +133,13 @@ export class YAMLCompletion { } } + // Support multiple doc per YAML + if (schema && schema.schema && schema.schema.schemaSequence && schema.schema.schemaSequence[currentDocIndex]) { + schema = new SchemaService.ResolvedSchema(schema.schema.schemaSequence[currentDocIndex]); + } + // proposals for properties if (node && node.type === 'object') { - // don't suggest keys when the cursor is just before the opening curly brace - if (node.offset === offset) { - return result; - } // don't suggest properties that are already present let properties = node.properties; properties.forEach(p => { @@ -362,7 +365,6 @@ export class YAMLCompletion { } } - private getValueCompletions(schema: SchemaService.ResolvedSchema, doc: Parser.JSONDocument, node: ASTNode, offset: number, document: TextDocument, collector: CompletionsCollector, types: { [type: string]: boolean }): void { let offsetForSeparator = offset; let parentKey: string = null; @@ -679,11 +681,11 @@ export class YAMLCompletion { } private getInsertTextForValue(value: any, separatorAfter: string): string { - var text = JSON.stringify(value, null, '\t'); + const text = value; if (text === '{}') { - return '{$1}' + separatorAfter; + return '{\n\t$1\n}' + separatorAfter; } else if (text === '[]') { - return '[$1]' + separatorAfter; + return '[\n\t$1\n]' + separatorAfter; } return this.getInsertTextForPlainText(text + separatorAfter); } @@ -767,7 +769,7 @@ export class YAMLCompletion { if (!addValue) { return propertyText; } - let resultText = propertyText + ': '; + let resultText = propertyText + ':'; let value; let nValueProposals = 0; @@ -804,23 +806,23 @@ export class YAMLCompletion { } switch (type) { case 'boolean': - value = '$1'; + value = ' $1'; break; case 'string': - value = '"$1"'; + value = ' $1'; break; case 'object': - value = '{$1}'; + value = '\n\t'; break; case 'array': - value = '[$1]'; + value = '\n\t- '; break; case 'number': case 'integer': - value = '${1:0}'; + value = ' ${1:0}'; break; case 'null': - value = '${1:null}'; + value = ' ${1:null}'; break; default: return propertyText; @@ -834,8 +836,8 @@ export class YAMLCompletion { } private getCurrentWord(document: TextDocument, offset: number) { - var i = offset - 1; - var text = document.getText(); + let i = offset - 1; + const text = document.getText(); while (i >= 0 && ' \t\n\r\v":{[,]}'.indexOf(text.charAt(i)) === -1) { i--; } @@ -853,7 +855,7 @@ export class YAMLCompletion { case Json.SyntaxKind.EOF: return ''; default: - return ','; + return ''; } } diff --git a/src/languageservice/yamlLanguageTypes.ts b/src/languageservice/yamlLanguageTypes.ts index 00d8366..18660ff 100644 --- a/src/languageservice/yamlLanguageTypes.ts +++ b/src/languageservice/yamlLanguageTypes.ts @@ -2,36 +2,65 @@ import { JSONDocument } from './parser/jsonParser'; import { ASTNode } from './jsonLanguageTypes'; export class SingleYAMLDocument extends JSONDocument { - public lines; - public errors; - public warnings; + public lines; + public errors; + public warnings; - constructor(lines: number[]) { - super(null, []); - this.lines = lines; - this.errors = []; - this.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 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); + public getNodeFromOffset(offset: number): ASTNode { + return super.getNodeFromOffset(offset, true); + } + + public getNodeFromOffsetEndInclusive(offset: number): ASTNode { + let collector: ASTNode[] = []; + let findNode = (node: ASTNode): ASTNode => { + if (offset >= node.offset && offset <= node.offset + node.length) { + let children = node.children; + for (let i = 0; i < children.length && children[i].offset <= offset; i++) { + let item = findNode(children[i]); + if (item) { + collector.push(item); + } + } + return node; + } + return null; + }; + let foundNode = findNode(this.root); + let currMinDist = Number.MAX_VALUE; + let currMinNode = null; + for (let possibleNode in collector) { + let currNode = collector[possibleNode]; + let minDist = (currNode.offset + currNode.length - offset) + (offset - currNode.offset); + if (minDist < currMinDist) { + currMinNode = currNode; + currMinDist = minDist; + } + } + return currMinNode || foundNode; } } export class YAMLDocument { - public documents: SingleYAMLDocument[] - public errors; - public warnings; + public documents: SingleYAMLDocument[] + public errors; + public warnings; - constructor(documents: SingleYAMLDocument[]) { - this.documents = documents; - this.errors = []; - this.warnings = []; - } + constructor(documents: SingleYAMLDocument[]) { + this.documents = documents; + this.errors = []; + this.warnings = []; + } } diff --git a/src/yaml-ast-parser/schema.ts b/src/yaml-ast-parser/schema.ts index a4a5262..8f4ed88 100644 --- a/src/yaml-ast-parser/schema.ts +++ b/src/yaml-ast-parser/schema.ts @@ -75,18 +75,19 @@ export class Schema { } static DEFAULT = null; - static create = function createSchema() { - var schemas, types; + static create = function createSchema(...args: [Schema | Schema[], Type[]] | [Type[]]) { + let schemas: Schema | Schema[]; + let types: Type[]; - switch (arguments.length) { + switch (args.length) { case 1: schemas = Schema.DEFAULT; - types = arguments[0]; + types = args[0]; break; case 2: - schemas = arguments[0]; - types = arguments[1]; + schemas = args[0]; + types = args[1]; break; default: @@ -109,4 +110,4 @@ export class Schema { explicit: types }); } -} \ No newline at end of file +} diff --git a/src/yamlMode.ts b/src/yamlMode.ts index 8537627..d557732 100644 --- a/src/yamlMode.ts +++ b/src/yamlMode.ts @@ -26,15 +26,16 @@ export function setupMode(defaults: LanguageServiceDefaultsImpl): void { let languageId = defaults.languageId; - // TODO: - // disposables.push(monaco.languages.registerCompletionItemProvider(languageId, new languageFeatures.CompletionAdapter(worker))); + 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.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.setLanguageConfiguration(languageId, richEditConfiguration)); + disposables.push(monaco.languages.setLanguageConfiguration(languageId, richEditConfiguration)); + + // Color adapter should be necessary most of the time: + // disposables.push(monaco.languages.registerColorProvider(languageId, new languageFeatures.DocumentColorAdapter(worker))); } diff --git a/test/index.html b/test/index.html index 475733c..df3b4cb 100644 --- a/test/index.html +++ b/test/index.html @@ -2,88 +2,142 @@ - - - + + + -

Monaco Editor YAML test page

- -
+

Monaco Editor YAML test page

+ +
- - - - + - + + + - var editor = monaco.editor.create(document.getElementById('container'), { - value: yaml, - language: 'yaml' - }); + + monaco.languages.yaml.yamlDefaults.setDiagnosticsOptions({ + enableSchemaRequest: true, + validate: true, + schemas: [{ + uri: "http://myserver/foo-schema.json", // id of the first schema + fileMatch: [modelUri.toString()], // associate with our model + schema: { + type: "object", + properties: { + p1: { + enum: ["v1", "v2"] + }, + p2: { + $ref: "http://myserver/bar-schema.json" // reference the second schema + } + } + } + }, { + uri: "http://myserver/bar-schema.json", // id of the first schema + schema: { + type: "object", + properties: { + q1: { + enum: ["x1", "x2"] + } + } + } + }] + }); + + require(["vs/editor/contrib/quickOpen/quickOpen"], async quickOpen => { + const NEVER_CANCEL_TOKEN = { + isCancellationRequested: false, + onCancellationRequested: () => Event.NONE, + }; + + let oldDecorations = []; + + async function _getSymbolForPosition(model, position) { + const symbols = await quickOpen.getDocumentSymbols( + model, + false, + NEVER_CANCEL_TOKEN, + ); + + function _recur(symbol) { + let target = symbol; + if (symbol && symbol.children && symbol.children.length) { + target = _recur(symbol.children.find(child => child.range.containsPosition(position))) || symbol; + + } + + return target; + } + + return _recur({ children: symbols }); + } + + editor.onDidChangeCursorSelection(async ({ selection }) => { + const model = editor.getModel(); + const position = selection.getPosition(); + const symbol = await _getSymbolForPosition(model, position); + + console.log(`${symbol.name}: ${symbol.range}`); + if (symbol && symbol.range) { + const decoration = { + range: symbol.range, + options: { + isWholeLine: false, + className: 'x-highlight-range', + }, + }; + + oldDecorations = editor.deltaDecorations( + oldDecorations, + decoration ? [decoration] : [], + ) + } + + }); + }); + }); + diff --git a/yarn.lock b/yarn.lock index 7954c44..c5e2d5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -140,10 +140,10 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -monaco-editor-core@0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/monaco-editor-core/-/monaco-editor-core-0.15.0.tgz#1ad5f130ed8efae9d9ed85d8a58a55067e14d983" - integrity sha512-s1zuo+p6Gl6IC4WJP6HBkr4pWULm+HdFfacB8vOFPQLLi2oJseO20UuSkxYuZTUJQIjvhNrQfLNAmPKLZaf7tg== +monaco-editor-core@0.15.5: + version "0.15.5" + resolved "https://registry.yarnpkg.com/monaco-editor-core/-/monaco-editor-core-0.15.5.tgz#145f1953a8e319282d92502252d68ef3486b6875" + integrity sha512-kM3KHRjj16cFdK5Z0EppKUu793JVMpsEesBSWlqdgrxcmjyDMXV6xK0oatPcAYp3eOfbbyjPhruxDXj85FKyIg== monaco-languages@1.6.0: version "1.6.0"