From ed907a90964689d68cacf88bafd48f3e40fa3fc4 Mon Sep 17 00:00:00 2001 From: Peng Xiao Date: Tue, 18 Dec 2018 18:49:13 +0800 Subject: [PATCH] fix: add colon position to key node --- .vscode/launch.json | 18 +++ src/languageservice/parser/jsonParser.ts | 1 + src/languageservice/parser/yamlParser.ts | 3 + .../services/yamlCompletion.ts | 2 +- src/languageservice/services/yamlHover.ts | 2 +- .../services/yamlValidation.ts | 2 +- src/languageservice/yamlLanguageTypes.ts | 117 +++++++++++++++++- src/yaml-ast-parser/loader.ts | 11 +- src/yaml-ast-parser/yamlAST.ts | 4 +- test/yamlDocument.test.ts | 62 ++++++++++ 10 files changed, 206 insertions(+), 16 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 test/yamlDocument.test.ts diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..4768b35 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "name": "vscode-jest-tests", + "request": "launch", + "args": ["--runInBand"], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "program": "${workspaceFolder}/node_modules/jest/bin/jest" + } + ] +} diff --git a/src/languageservice/parser/jsonParser.ts b/src/languageservice/parser/jsonParser.ts index c5ce19a..8ab60fc 100644 --- a/src/languageservice/parser/jsonParser.ts +++ b/src/languageservice/parser/jsonParser.ts @@ -142,6 +142,7 @@ export class NumberASTNodeImpl extends ASTNodeImpl implements NumberASTNode { export class StringASTNodeImpl extends ASTNodeImpl implements StringASTNode { public type: 'string' = 'string'; public value: string; + public isKey: boolean; constructor(parent: ASTNode, offset: number, length?: number) { super(parent, offset, length); diff --git a/src/languageservice/parser/yamlParser.ts b/src/languageservice/parser/yamlParser.ts index b15fe25..16ed99d 100644 --- a/src/languageservice/parser/yamlParser.ts +++ b/src/languageservice/parser/yamlParser.ts @@ -61,6 +61,8 @@ function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode { instance.endPosition - key.startPosition ); + result.colonOffset = key.colonPosition; + // Technically, this is an arbitrary node in YAML // I doubt we would get a better string representation by parsing it const keyNode = new StringASTNodeImpl( @@ -69,6 +71,7 @@ function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode { key.endPosition - key.startPosition ); keyNode.value = key.value; + keyNode.isKey = true; // TODO: calculate the correct NULL range. const valueNode = instance.value diff --git a/src/languageservice/services/yamlCompletion.ts b/src/languageservice/services/yamlCompletion.ts index 1ca6fd8..b10d15e 100644 --- a/src/languageservice/services/yamlCompletion.ts +++ b/src/languageservice/services/yamlCompletion.ts @@ -59,7 +59,7 @@ export class YAMLCompletion { public configure(settings: LanguageSettings) { if (settings) { - this.completionEnabled = settings.completion; + this.completionEnabled = settings.completion !== false; } } diff --git a/src/languageservice/services/yamlHover.ts b/src/languageservice/services/yamlHover.ts index 427a869..a2f6110 100644 --- a/src/languageservice/services/yamlHover.ts +++ b/src/languageservice/services/yamlHover.ts @@ -31,7 +31,7 @@ export class YAMLHover { public configure(languageSettings: LanguageSettings) { if (languageSettings) { - this.shouldHover = !!languageSettings.hover; + this.shouldHover = languageSettings.hover !== false; } } diff --git a/src/languageservice/services/yamlValidation.ts b/src/languageservice/services/yamlValidation.ts index 6ff703c..d429a53 100644 --- a/src/languageservice/services/yamlValidation.ts +++ b/src/languageservice/services/yamlValidation.ts @@ -23,7 +23,7 @@ export class YAMLValidation { public configure(raw: LanguageSettings) { if (raw) { - this.validationEnabled = raw.validate; + this.validationEnabled = raw.validate !== false; } } diff --git a/src/languageservice/yamlLanguageTypes.ts b/src/languageservice/yamlLanguageTypes.ts index 40c8f24..4eeb95c 100644 --- a/src/languageservice/yamlLanguageTypes.ts +++ b/src/languageservice/yamlLanguageTypes.ts @@ -1,8 +1,9 @@ import { ASTNode } from './jsonLanguageTypes'; import { JSONDocument, IProblem } from './parser/jsonParser'; +import { getPosition } from './utils/documentPositionCalculator'; export class SingleYAMLDocument extends JSONDocument { - public lines; + public lines: number[]; public errors: IProblem[]; public warnings: IProblem[]; @@ -23,6 +24,116 @@ export class SingleYAMLDocument extends JSONDocument { return super.getNodeFromOffset(offset, true); } + // getNodeFromOffsetEndInclusive(offset: number): ASTNode { + // if (!this.root) { + // return; + // } + // if ( + // offset < this.root.offset || + // offset > this.root.offset + this.root.length + // ) { + // // We somehow are completely outside the document + // // This is unexpected + // console.log('Attempting to resolve node outside of document'); + // return null; + // } + + // function* sliding2(nodes: ASTNode[]) { + // for (let i = 0; i < nodes.length; i ++) { + // yield [nodes[i], i === nodes.length ? null : nodes[i + 1]]; + // } + // } + + // const onLaterLine = (offset: number, node: ASTNode) => { + // const { line: actualLine } = getPosition(offset, this.lines); + // const { line: nodeEndLine } = getPosition( + // node.offset + node.length, + // this.lines + // ); + + // return actualLine > nodeEndLine; + // }; + + // let findNode = (nodes: ASTNode[]): ASTNode => { + // if (nodes.length === 0) { + // return null; + // } + + // const gen = sliding2(nodes); + + // for (let [first, second] of gen) { + // const end = second + // ? second.offset + // : first.parent.offset + first.parent.length; + // if (offset >= first.offset && offset < end) { + // const children = first.children; + + // const foundChild = findNode(children); + + // if (!foundChild && onLaterLine(offset, first)) { + // return this.getNodeByIndent(this.lines, offset, this.root); + // } + + // return foundChild || first; + // } + // } + + // return null; + // }; + + // return findNode(this.root.children) || this.root; + // } + + // private getNodeByIndent = ( + // lines: number[], + // offset: number, + // node: ASTNode + // ) => { + // const { line, column: indent } = getPosition(offset, this.lines); + + // const children = node.children; + + // function findNode(children: ASTNode[]) { + // if (children.length > 0) { + + // } + // for (let idx = 0; idx < children.length; idx++) { + // const child = children[idx]; + + // const { line: childLine, column: childCol } = getPosition( + // child.offset, + // lines + // ); + + // if (childCol > indent) { + // return null; + // } + + // const newChildren = child.children; + // 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; + // }; + public getNodeFromOffsetEndInclusive(offset: number): ASTNode { const collector: ASTNode[] = []; const findNode = (node: ASTNode): ASTNode => { @@ -60,12 +171,8 @@ export class SingleYAMLDocument extends JSONDocument { export class YAMLDocument { public documents: SingleYAMLDocument[]; - public errors; - public warnings; constructor(documents: SingleYAMLDocument[]) { this.documents = documents; - this.errors = []; - this.warnings = []; } } diff --git a/src/yaml-ast-parser/loader.ts b/src/yaml-ast-parser/loader.ts index 17769d4..16a76d8 100644 --- a/src/yaml-ast-parser/loader.ts +++ b/src/yaml-ast-parser/loader.ts @@ -492,11 +492,7 @@ function storeMappingPair( }); _result.mappings.push(mapping); - _result.endPosition = valueNode - ? valueNode.endPosition - : keyNode.endPosition + 1; // FIXME.workaround should be position of ':' indeed - // } - + _result.endPosition = mapping.endPosition; return _result; } @@ -646,7 +642,8 @@ function readPlainScalar(state: State, nodeIndent, withinFlowCollection) { _lineIndent, _kind = state.kind, _result = state.result, - ch; + ch, + _colonPosition = -1; const state_result = ast.newScalar(); state_result.plainScalar = true; state.result = state_result; @@ -694,6 +691,7 @@ function readPlainScalar(state: State, nodeIndent, withinFlowCollection) { is_WS_OR_EOL(following) || (withinFlowCollection && is_FLOW_INDICATOR(following)) ) { + _colonPosition = state.position; break; } } else if (0x23 /* # */ === ch) { @@ -750,6 +748,7 @@ function readPlainScalar(state: State, nodeIndent, withinFlowCollection) { state_result.startPosition, state_result.endPosition ); + state_result.colonPosition = _colonPosition; return true; } diff --git a/src/yaml-ast-parser/yamlAST.ts b/src/yaml-ast-parser/yamlAST.ts index 1eb4613..b803198 100644 --- a/src/yaml-ast-parser/yamlAST.ts +++ b/src/yaml-ast-parser/yamlAST.ts @@ -20,6 +20,7 @@ export interface YAMLNode extends YAMLDocument { startPosition: number; endPosition: number; kind: Kind; + colonPosition?: number; // Nearest colon position. anchorId?: string; valueObject?: any; parent: YAMLNode; @@ -63,8 +64,7 @@ export interface YamlMap extends YAMLNode { mappings: YAMLMapping[]; } export function newMapping(key: YAMLScalar, value: YAMLNode): YAMLMapping { - const end = value ? value.endPosition : key.endPosition + 1; // FIXME.workaround, end should be defied by position of ':' - // console.log('key: ' + key.value + ' ' + key.startPosition + '..' + key.endPosition + ' ' + value + ' end: ' + end); + const end = value ? value.endPosition : key.colonPosition + 1; const node = { key, value, diff --git a/test/yamlDocument.test.ts b/test/yamlDocument.test.ts new file mode 100644 index 0000000..7a20e78 --- /dev/null +++ b/test/yamlDocument.test.ts @@ -0,0 +1,62 @@ +import { TextDocument } from 'vscode-languageserver'; +import { parse as parseYAML } from '../src/languageservice/parser/yamlParser'; + +describe('SingleYAMLDocument tests', () => { + function setup(content: string) { + return TextDocument.create( + 'file://~/Desktop/vscode-k8s/test.yaml', + 'yaml', + 0, + content + ); + } + + describe('getNodeFromOffsetEndInclusive', () => { + const content = `a : + b: + + `; + function parseSetup(offset: number) { + const yamlDocs = parseYAML(content); + + // Should be one doc only + expect(yamlDocs.documents.length).toBe(1); + return yamlDocs.documents[0].getNodeFromOffsetEndInclusive(offset); + } + + it('0', () => { + const node = parseSetup(0); + expect(node.value).toBe('a'); + expect(node.type).toBe('string'); + }); + + it('1', () => { + const node = parseSetup(1); + expect(node.value).toBe('a'); + expect(node.type).toBe('string'); + }); + + it('2', () => { + const node = parseSetup(2); + expect(node.type).toBe('property'); + }); + + it('6', () => { + const node = parseSetup(6); + expect(node.value).toBe('b'); + expect(node.type).toBe('string'); + }); + + it('7', () => { + const node = parseSetup(7); + expect(node.value).toBe('b'); + expect(node.type).toBe('string'); + }); + + it('8', () => { + const node = parseSetup(8); + expect(node.value).toBe('b'); + expect(node.type).toBe('string'); + }); + }); +});