Skip to content

Commit

Permalink
Add support for multiple documents per file
Browse files Browse the repository at this point in the history
  • Loading branch information
adamvoss committed Jul 15, 2017
1 parent 79d3ed7 commit c680399
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 27 deletions.
47 changes: 41 additions & 6 deletions src/parser/yamlParser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

import { JSONDocument, ASTNode, ErrorCode, BooleanASTNode, NullASTNode, ArrayASTNode, NumberASTNode, ObjectASTNode, PropertyASTNode, StringASTNode } from '../../vscode-json-languageservice/src/parser/jsonParser';
import { JSONDocument, ASTNode, ErrorCode, BooleanASTNode, NullASTNode, ArrayASTNode, NumberASTNode, ObjectASTNode, PropertyASTNode, StringASTNode, IError, IApplicableSchema } from '../../vscode-json-languageservice/src/parser/jsonParser';
import { JSONSchema } from '../../vscode-json-languageservice/src/jsonSchema';

import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
Expand All @@ -10,7 +11,7 @@ import { Kind } from 'yaml-ast-parser'

import { getLineStartPositions, getPosition } from '../documentPositionCalculator'

export class YAMLDocument extends JSONDocument {
export class SingleYAMLDocument extends JSONDocument {
private lines;

constructor(lines: number[]) {
Expand Down Expand Up @@ -257,7 +258,7 @@ function convertError(e: Yaml.YAMLException) {
}

function createJSONDocument(yamlDoc: Yaml.YAMLNode, startPositions: number[]){
let _doc = new YAMLDocument(startPositions);
let _doc = new SingleYAMLDocument(startPositions);
_doc.root = recursivelyBuildAst(null, yamlDoc)

if (!_doc.root) {
Expand All @@ -276,12 +277,46 @@ function createJSONDocument(yamlDoc: Yaml.YAMLNode, startPositions: number[]){
return _doc;
}

export function parse(text: string): JSONDocument {
export class YAMLDocument {
public documents: JSONDocument[]

constructor(documents: JSONDocument[]){
this.documents = documents;
}

get errors(): IError[]{
return (<IError[]>[]).concat(...this.documents.map(d => d.errors))
}

get warnings(): IError[]{
return (<IError[]>[]).concat(...this.documents.map(d => d.warnings))
}

public getNodeFromOffset(offset: number): ASTNode {
// Depends on the documents being sorted
for (let element of this.documents) {
if (offset <= element.root.end) {
return element.getNodeFromOffset(offset)
}
}

return undefined;
}

public validate(schema: JSONSchema, matchingSchemas: IApplicableSchema[] = null, offset: number = -1): void {
this.documents.forEach(doc => {
doc.validate(schema, matchingSchemas, offset)
});
}
}

export function parse(text: string): YAMLDocument {

const startPositions = getLineStartPositions(text)
// This is documented to return a YAMLNode even though the
// typing only returns a YAMLDocument
const yamlDoc = <Yaml.YAMLNode>Yaml.safeLoad(text, {})
const yamlDocs = []
Yaml.loadAll(text, doc => yamlDocs.push(doc), {})

return createJSONDocument(yamlDoc, startPositions);
return new YAMLDocument(yamlDocs.map(doc => createJSONDocument(doc, startPositions)));
}
10 changes: 5 additions & 5 deletions src/test/nodeOffset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import YamlParser = require('../parser/yamlParser');


suite("Get Node from Offset", () => {
test('End Inclusive', () => {
test('', () => {
const str = `outer:
inner:
`
const document = YamlParser.parse(str)
const document = YamlParser.parse(str).documents[0]

let assertionCount = 1;

const assertNameAndType = (offset, path: string[], type: string) => {
const node = document.getNodeFromOffsetEndInclusive(offset);
const node = document.getNodeFromOffset(offset);
assert.deepEqual(node.type, type, `${assertionCount}`)
assert.deepStrictEqual(node.getPath(), path, `${assertionCount}`)
assertionCount++;
Expand All @@ -27,7 +27,7 @@ suite("Get Node from Offset", () => {

assertNameAndType(10, ["outer", "inner"], "string")
// TODO: These should both be object
assertNameAndType(19, [], "property")
assertNameAndType(21, ["outer"], "property")
// assertNameAndType(19, [], "property") //https://github.com/mulesoft-labs/yaml-ast-parser/issues/25
// assertNameAndType(21, ["outer"], "property") //https://github.com/mulesoft-labs/yaml-ast-parser/issues/25
})
})
42 changes: 28 additions & 14 deletions src/test/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,17 @@ suite('YAML Parser', () => {
});

test('implicit null in array', function () {
let result = YamlParser.parse(`- 1\n- 2\n-\n- 4`);
let result = YamlParser.parse(`- 1\n- 2\n-\n- 4`).documents[0];
assert.deepStrictEqual((<Parser.ArrayASTNode>result.root).items.map(x => x.getValue()), [1, 2, null, 4])

// NOTE: In the future we can hope this tests breaks
// https://github.com/nodeca/js-yaml/issues/321
result = YamlParser.parse(`[1,'',,4,]`);
result = YamlParser.parse(`[1,'',,4,]`).documents[0];
assert.deepStrictEqual((<Parser.ArrayASTNode>result.root).items.map(x => x.getValue()), [1, '', null, 4])
})

test('implicit null in mapping', function () {
let result = YamlParser.parse(`{key,}`);
let result = YamlParser.parse(`{key,}`).documents[0];
const properties = (<Parser.ObjectASTNode>result.root).properties
assert.strictEqual(properties.length, 1)
assert.strictEqual(properties[0].key.value, "key")
Expand All @@ -183,7 +183,7 @@ suite('YAML Parser', () => {
test('Nested AST', function () {

var content = '{\n\t"key" : {\n\t"key2": 42\n\t}\n}';
var result = YamlParser.parse(content);
var result = YamlParser.parse(content).documents[0];

assert.strictEqual(result.errors.length, 0);

Expand All @@ -200,7 +200,7 @@ suite('YAML Parser', () => {

test('Nested AST in Array', function () {

var result = YamlParser.parse('{"key":[{"key2":42}]}');
var result = YamlParser.parse('{"key":[{"key2":42}]}').documents[0];

assert.strictEqual(result.errors.length, 0);

Expand All @@ -214,7 +214,7 @@ suite('YAML Parser', () => {
test('Multiline', function () {

var content = '{\n\t\n}';
var result = YamlParser.parse(content);
var result = YamlParser.parse(content).documents[0];

assert.strictEqual(result.errors.length, 0);

Expand All @@ -223,7 +223,7 @@ suite('YAML Parser', () => {
assert.notEqual(node, null);

content = '{\n"first":true\n\n}';
result = YamlParser.parse(content);
result = YamlParser.parse(content).documents[0];

node = result.getNodeFromOffset(content.length - 2);
assert.equal(node.type, /*'object'*/ 'property');
Expand All @@ -235,7 +235,7 @@ suite('YAML Parser', () => {
test('Expand errors to entire tokens', function () {

var content = '{\n"key" 32,\nerror\n}';
var result = YamlParser.parse(content);
var result = YamlParser.parse(content).documents[0];
assert.equal(result.errors.length, 1);
assert.equal(result.errors[0].location.start, content.indexOf('32'));
assert.equal(result.errors[0].location.end, content.length);
Expand Down Expand Up @@ -1337,7 +1337,7 @@ suite('YAML Parser', () => {
test('parse with comments', function () {

function parse<T>(v: string): T {
var result = YamlParser.parse(v);
var result = YamlParser.parse(v).documents[0];
assert.equal(result.errors.length, 0);
return <T>result.root.getValue();
}
Expand All @@ -1360,19 +1360,19 @@ suite('YAML Parser', () => {
assert.equal(result.errors.length, expectedErrors);
}

assertParse('// comment\n{\n"far": "boo"\n}', 3);
assertParse('/* comm\nent\nent */\n{\n"far": "boo"\n}', 3);
assertParse('// comment\n{\n"far": "boo"\n}', 4);
assertParse('/* comm\nent\nent */\n{\n"far": "boo"\n}', 4);
assertParse('{\n"far": "boo"\n}', 0);
});

suite('anchor references', () => {
test('expands reference', function () {
isValid('- foo: &ref 5\n- bar: *ref')

const result = YamlParser.parse('- foo: &ref 5\n- bar: *ref').root
const expected = YamlParser.parse('- foo: 5\n- bar: 5').root
const actualValues = YamlParser.parse('- foo: &ref 5\n- bar: *ref').documents.map(d => d.root.getValue())
const expectedValues = YamlParser.parse('- foo: 5\n- bar: 5').documents.map(d => d.root.getValue())

assert.deepStrictEqual(result.getValue(), expected.getValue())
assert.deepStrictEqual(actualValues, expectedValues)
})

test('errors on missing reference', function () {
Expand All @@ -1381,4 +1381,18 @@ suite('YAML Parser', () => {
})
})

suite('Multiple Documents', () => {
test.only("are parsed", function(){
const input = `---
value: 1
...
---
value: 2
...`
isValid(input);
const result = YamlParser.parse(input)
console.log(result)
})
});

});
14 changes: 12 additions & 2 deletions src/yamlLanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {schemaContributions} from '../vscode-json-languageservice/src/services/c
import {JSONSchemaService} from '../vscode-json-languageservice/src/services/jsonSchemaService';
import {JSONWorkerContribution, JSONPath, Segment, CompletionsCollector} from '../vscode-json-languageservice/src/jsonContributions';

export type YAMLDocument = {};
export type JSONDocument = {}
export type YAMLDocument = { documents: JSONDocument[]}
export {JSONSchema, JSONWorkerContribution, JSONPath, Segment, CompletionsCollector};
export {TextDocument, Position, CompletionItem, CompletionList, Hover, Range, SymbolInformation, Diagnostic,
TextEdit, FormattingOptions, MarkedString};
Expand Down Expand Up @@ -156,6 +157,15 @@ export function getLanguageService(params: LanguageServiceParams): LanguageServi
let jsonDocumentSymbols = new JSONDocumentSymbols(jsonSchemaService);
let jsonValidation = new JSONValidation(jsonSchemaService, promise);


function doValidation(textDocument: TextDocument, yamlDocument: YAMLDocument) {
var validate: (JSONDocument) => Thenable<Diagnostic[]> =
jsonValidation.doValidation.bind(jsonValidation, textDocument)
const validationResults = yamlDocument.documents.map(d => validate(d))
const resultsPromise = promise.all(validationResults);
return resultsPromise.then(res => (<Diagnostic[]>[]).concat(...res))
}

return {
configure: (settings: LanguageSettings) => {
jsonSchemaService.clearExternalSchemas();
Expand All @@ -167,7 +177,7 @@ export function getLanguageService(params: LanguageServiceParams): LanguageServi
jsonValidation.configure(settings);
},
resetSchema: (uri: string) => jsonSchemaService.onResourceChange(uri),
doValidation: jsonValidation.doValidation.bind(jsonValidation),
doValidation: doValidation,
parseYAMLDocument : (document: TextDocument) => parseYAML(document.getText()),
doResolve: jsonCompletion.doResolve.bind(jsonCompletion),
doComplete: jsonCompletion.doComplete.bind(jsonCompletion),
Expand Down

0 comments on commit c680399

Please sign in to comment.