diff --git a/packages/ast/README.md b/packages/ast/README.md index ad828cdc..eb1dbbff 100644 --- a/packages/ast/README.md +++ b/packages/ast/README.md @@ -42,8 +42,8 @@ const xmlText = ` `; -const { cst } = parse(xmlText); -const xmlDocAst = buildAst(cst); +const { cst, tokenVector } = parse(xmlText); +const xmlDocAst = buildAst(cst, tokenVector); console.log(xmlDocAst.rootElement.name); // -> note // A Visitor allows us to invoke actions on the XML ASTNodes without worrying about diff --git a/packages/ast/api.d.ts b/packages/ast/api.d.ts index 1870ebf2..8989ac1a 100644 --- a/packages/ast/api.d.ts +++ b/packages/ast/api.d.ts @@ -1,12 +1,14 @@ +import { IToken } from "chevrotain"; import { DocumentCstNode } from "@xml-tools/parser"; /** * Builds an XML Ast from an XML Cst. * Can process even partial CSTs... - * - * @param docCst */ -export function buildAst(docCst: DocumentCstNode): XMLDocument; +export function buildAst( + docCst: DocumentCstNode, + tokenVector: IToken[] +): XMLDocument; /** * An Abstract Syntax Tree structure @@ -85,6 +87,23 @@ declare interface XMLElement { // Will not exist if any of the closing "brackets" are missing // - e.g in a self closing element. readonly closeBody?: XMLToken; + + // Describes the range of the attributes section: + // This starts one character **after** the opening name token + // and ends one character before the open body closing '>' or self closing '/> + // Examples: + // Some content + // <===== attributesRange =====> + // + // <===== attributesRange =====> + readonly attributesRange?: SourceRange; + + // Same as attributesRange except this property will be used for partially valid + // XMLElements when the AstBuilder cannot be certain what is the exact attributes range. + // - Only one of the attributeRanges properties may exist at the same time. + // - It is possible that in some cases it won't be possible to even guess the attributes range. + // In that scenario neither of the properties will exist. + readonly guessedAttributesRange?: SourceRange; }; readonly position: SourcePosition; } @@ -114,6 +133,11 @@ declare interface XMLAttribute { readonly position: SourcePosition; } +declare interface SourceRange { + readonly startOffset: number; + readonly endOffset: number; +} + declare interface SourcePosition { readonly startOffset: number; readonly endOffset: number; diff --git a/packages/ast/lib/build-ast.js b/packages/ast/lib/build-ast.js index 2184e0dc..602b5213 100644 --- a/packages/ast/lib/build-ast.js +++ b/packages/ast/lib/build-ast.js @@ -1,5 +1,6 @@ const { BaseXmlCstVisitor } = require("@xml-tools/parser"); const { + last, forEach, reduce, map, @@ -8,14 +9,16 @@ const { isEmpty, isArray } = require("lodash"); - +const { findNextTextualToken } = require("@xml-tools/common"); const { getAstChildrenReflective } = require("./utils"); /** * @param {DocumentCstNode} docCst + * @param {IToken[]} tokenVector * @returns {XMLDocument} */ -function buildAst(docCst) { +function buildAst(docCst, tokenVector) { + AstBuilder.setState({ tokenVector }); const xmlDocAst = AstBuilder.visit(docCst); if (xmlDocAst.rootElement !== invalidSyntax) { @@ -29,6 +32,10 @@ class CstToAstVisitor extends BaseXmlCstVisitor { super(); } + setState({ tokenVector }) { + this.tokenVector = tokenVector; + } + visit(cstNode) { return super.visit(cstNode, cstNode.location); } @@ -104,7 +111,7 @@ class CstToAstVisitor extends BaseXmlCstVisitor { } /** - * @param ctx {ElementCstNode} + * @param ctx {ElementCtx} * @param location {SourcePosition} */ element(ctx, location) { @@ -129,49 +136,10 @@ class CstToAstVisitor extends BaseXmlCstVisitor { astNode.textContents = textContents; } - if (ctx.Name !== undefined && ctx.Name[0].isInsertedInRecovery !== true) { - const openNameToken = ctx.Name[0]; - astNode.syntax.openName = toXMLToken(openNameToken); - const nsParts = nsToParts(openNameToken.image); - if (nsParts !== null) { - astNode.ns = nsParts.ns; - astNode.name = nsParts.name; - } else { - astNode.name = openNameToken.image; - } - } + handleElementOpenCloseNameRanges(astNode, ctx); + handleElementOpenCloseBodyRanges(astNode, ctx); + handleElementAttributeRanges(astNode, ctx, this.tokenVector); - if ( - ctx.END_NAME !== undefined && - ctx.END_NAME[0].isInsertedInRecovery !== true - ) { - astNode.syntax.closeName = toXMLToken(ctx.END_NAME[0]); - } - - /* istanbul ignore else - Defensive Coding */ - if (exists(ctx.OPEN)) { - let openBodyCloseTok = undefined; - /* istanbul ignore else - Defensive Coding */ - if (exists(ctx.START_CLOSE)) { - openBodyCloseTok = ctx.START_CLOSE[0]; - } else if (exists(ctx.SLASH_CLOSE)) { - openBodyCloseTok = ctx.SLASH_CLOSE[0]; - } - - if (openBodyCloseTok !== undefined) { - astNode.syntax.openBody = { - ...startOfXMLToken(ctx.OPEN[0]), - ...endOfXMLToken(openBodyCloseTok) - }; - } - } - - if (exists(ctx.SLASH_OPEN) && exists(ctx.END)) { - astNode.syntax.closeBody = { - ...startOfXMLToken(ctx.SLASH_OPEN[0]), - ...endOfXMLToken(ctx.END[0]) - }; - } setChildrenParent(astNode); return astNode; @@ -299,7 +267,6 @@ function updateNamespaces(element, prevNamespaces = []) { } /** - * * @param {chevrotain.IToken} token */ function toXMLToken(token) { @@ -352,6 +319,98 @@ function nsToParts(text) { */ const invalidSyntax = null; +/** + * @param {XMLElement} astNode + * @param {ElementCtx} ctx + */ +function handleElementOpenCloseNameRanges(astNode, ctx) { + if (ctx.Name !== undefined && ctx.Name[0].isInsertedInRecovery !== true) { + const openNameToken = ctx.Name[0]; + astNode.syntax.openName = toXMLToken(openNameToken); + const nsParts = nsToParts(openNameToken.image); + if (nsParts !== null) { + astNode.ns = nsParts.ns; + astNode.name = nsParts.name; + } else { + astNode.name = openNameToken.image; + } + } + + if ( + ctx.END_NAME !== undefined && + ctx.END_NAME[0].isInsertedInRecovery !== true + ) { + astNode.syntax.closeName = toXMLToken(ctx.END_NAME[0]); + } +} + +/** + * @param {XMLElement} astNode + * @param {ElementCtx} ctx + */ +function handleElementOpenCloseBodyRanges(astNode, ctx) { + /* istanbul ignore else - Defensive Coding */ + if (exists(ctx.OPEN)) { + let openBodyCloseTok = undefined; + /* istanbul ignore else - Defensive Coding */ + if (exists(ctx.START_CLOSE)) { + openBodyCloseTok = ctx.START_CLOSE[0]; + } else if (exists(ctx.SLASH_CLOSE)) { + openBodyCloseTok = ctx.SLASH_CLOSE[0]; + } + + if (openBodyCloseTok !== undefined) { + astNode.syntax.openBody = { + ...startOfXMLToken(ctx.OPEN[0]), + ...endOfXMLToken(openBodyCloseTok) + }; + } + + if (exists(ctx.SLASH_OPEN) && exists(ctx.END)) { + astNode.syntax.closeBody = { + ...startOfXMLToken(ctx.SLASH_OPEN[0]), + ...endOfXMLToken(ctx.END[0]) + }; + } + } +} + +/** + * @param {XMLElement} astNode + * @param {ElementCtx} ctx + * @param {IToken[]} tokenVector + */ +function handleElementAttributeRanges(astNode, ctx, tokenVector) { + if (exists(ctx.Name)) { + const startOffset = ctx.Name[0].endOffset + 2; + // Valid `attributesRange` exists + if (exists(ctx.START_CLOSE) || exists(ctx.SLASH_CLOSE)) { + const endOffset = + (exists(ctx.START_CLOSE) + ? ctx.START_CLOSE[0].startOffset + : ctx.SLASH_CLOSE[0].startOffset) - 1; + astNode.syntax.attributesRange = { startOffset, endOffset }; + } + // Have to scan-ahead and guess where the attributes range ends + else { + const hasAttributes = isArray(ctx.attribute); + const lastKnownAttribRangeTokenEnd = hasAttributes + ? last(ctx.attribute).location.endOffset + : ctx.Name[0].endOffset; + const nextTextualToken = findNextTextualToken( + tokenVector, + lastKnownAttribRangeTokenEnd + ); + if (nextTextualToken !== null) { + astNode.syntax.guessedAttributesRange = { + startOffset, + endOffset: nextTextualToken.endOffset - 1 + }; + } + } + } +} + module.exports = { buildAst: buildAst }; diff --git a/packages/ast/package.json b/packages/ast/package.json index 6f4b1a82..7781bacd 100644 --- a/packages/ast/package.json +++ b/packages/ast/package.json @@ -11,6 +11,7 @@ "license": "Apache-2.0", "typings": "./api.d.ts", "dependencies": { + "@xml-tools/common": "^0.0.1", "@xml-tools/parser": "^0.4.0", "lodash": "4.17.15" }, diff --git a/packages/ast/scripts/update-snapshots.js b/packages/ast/scripts/update-snapshots.js index 3a07d146..61ae7312 100644 --- a/packages/ast/scripts/update-snapshots.js +++ b/packages/ast/scripts/update-snapshots.js @@ -21,8 +21,8 @@ xmlSampleFiles.forEach(fileDesc => { const xmlInput = readFileSync(fileDesc.path, "utf8"); const simpleNewLinesInput = xmlInput.replace(/\r\n/g, "\n"); console.log(`Reading <${fileDesc.path}>`); - const { cst } = parse(simpleNewLinesInput); - const ast = buildAst(cst); + const { cst, tokenVector } = parse(simpleNewLinesInput); + const ast = buildAst(cst, tokenVector); modifyAstForAssertions(ast); const snapshotOutput = `module.exports = { ast : ${JSON.stringify(ast)}}`; const formattedSnapshotOutput = format(snapshotOutput, { parser: "babel" }); diff --git a/packages/ast/test/sample-test.js b/packages/ast/test/sample-test.js index 7cde94b8..5ad52a0c 100644 --- a/packages/ast/test/sample-test.js +++ b/packages/ast/test/sample-test.js @@ -15,12 +15,14 @@ function executeSampleTest(dirPath, assertNoErrors) { const inputPath = resolve(dirPath, "input.xml"); const inputText = readFileSync(inputPath).toString("utf8"); const simpleNewLinesInput = inputText.replace(/\r\n/g, "\n"); - const { cst, lexErrors, parseErrors } = parse(simpleNewLinesInput); + const { cst, tokenVector, lexErrors, parseErrors } = parse( + simpleNewLinesInput + ); if (assertNoErrors === true) { expect(lexErrors).to.be.empty; expect(parseErrors).to.be.empty; } - const ast = buildAst(cst); + const ast = buildAst(cst, tokenVector); assertParentPropsAreValid(ast); modifyAstForAssertions(ast); const expectedOutput = require(resolve(dirPath, "output.js")).ast; diff --git a/packages/ast/test/snapshots/invalid/attributes-no-value-last/output.js b/packages/ast/test/snapshots/invalid/attributes-no-value-last/output.js index 3178ed1e..3cf7a4dc 100644 --- a/packages/ast/test/snapshots/invalid/attributes-no-value-last/output.js +++ b/packages/ast/test/snapshots/invalid/attributes-no-value-last/output.js @@ -41,7 +41,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 40, endOffset: 43 }, openBody: { startOffset: 0, endOffset: 37 }, - closeBody: { startOffset: 38, endOffset: 44 } + closeBody: { startOffset: 38, endOffset: 44 }, + attributesRange: { startOffset: 6, endOffset: 36 } } }, position: { startOffset: 0, endOffset: 45 } diff --git a/packages/ast/test/snapshots/invalid/attributes-no-value-middle/output.js b/packages/ast/test/snapshots/invalid/attributes-no-value-middle/output.js index 9df15424..774369b4 100644 --- a/packages/ast/test/snapshots/invalid/attributes-no-value-middle/output.js +++ b/packages/ast/test/snapshots/invalid/attributes-no-value-middle/output.js @@ -41,7 +41,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 40, endOffset: 43 }, openBody: { startOffset: 0, endOffset: 37 }, - closeBody: { startOffset: 38, endOffset: 44 } + closeBody: { startOffset: 38, endOffset: 44 }, + attributesRange: { startOffset: 6, endOffset: 36 } } }, position: { startOffset: 0, endOffset: 45 } diff --git a/packages/ast/test/snapshots/invalid/attributes-no-value-no-eql-last/output.js b/packages/ast/test/snapshots/invalid/attributes-no-value-no-eql-last/output.js index b2d8d829..eb3c86e9 100644 --- a/packages/ast/test/snapshots/invalid/attributes-no-value-no-eql-last/output.js +++ b/packages/ast/test/snapshots/invalid/attributes-no-value-no-eql-last/output.js @@ -41,7 +41,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 39, endOffset: 42 }, openBody: { startOffset: 0, endOffset: 36 }, - closeBody: { startOffset: 37, endOffset: 43 } + closeBody: { startOffset: 37, endOffset: 43 }, + attributesRange: { startOffset: 6, endOffset: 35 } } }, position: { startOffset: 0, endOffset: 44 } diff --git a/packages/ast/test/snapshots/invalid/attributes-no-value-no-eql-middle/output.js b/packages/ast/test/snapshots/invalid/attributes-no-value-no-eql-middle/output.js index 6844b3c5..8c7d182e 100644 --- a/packages/ast/test/snapshots/invalid/attributes-no-value-no-eql-middle/output.js +++ b/packages/ast/test/snapshots/invalid/attributes-no-value-no-eql-middle/output.js @@ -41,7 +41,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 39, endOffset: 42 }, openBody: { startOffset: 0, endOffset: 36 }, - closeBody: { startOffset: 37, endOffset: 43 } + closeBody: { startOffset: 37, endOffset: 43 }, + attributesRange: { startOffset: 6, endOffset: 35 } } }, position: { startOffset: 0, endOffset: 44 } diff --git a/packages/ast/test/snapshots/invalid/attributes-no-value-not-closed-nested/output.js b/packages/ast/test/snapshots/invalid/attributes-no-value-not-closed-nested/output.js index 4afbf31d..dfcec7a1 100644 --- a/packages/ast/test/snapshots/invalid/attributes-no-value-not-closed-nested/output.js +++ b/packages/ast/test/snapshots/invalid/attributes-no-value-not-closed-nested/output.js @@ -36,7 +36,8 @@ module.exports = { textContents: [], position: { startOffset: 10, endOffset: 35 }, syntax: { - openName: { image: "nested", startOffset: 11, endOffset: 16 } + openName: { image: "nested", startOffset: 11, endOffset: 16 }, + guessedAttributesRange: { startOffset: 18, endOffset: 40 } } }, { @@ -57,7 +58,8 @@ module.exports = { openName: { image: "nest2", startOffset: 42, endOffset: 46 }, closeName: { image: "nest2", startOffset: 61, endOffset: 65 }, openBody: { startOffset: 41, endOffset: 47 }, - closeBody: { startOffset: 59, endOffset: 66 } + closeBody: { startOffset: 59, endOffset: 66 }, + attributesRange: { startOffset: 48, endOffset: 46 } } }, { @@ -89,7 +91,8 @@ module.exports = { openName: { image: "nested3", startOffset: 73, endOffset: 79 }, closeName: { image: "nested3", startOffset: 94, endOffset: 100 }, openBody: { startOffset: 72, endOffset: 90 }, - closeBody: { startOffset: 92, endOffset: 101 } + closeBody: { startOffset: 92, endOffset: 101 }, + attributesRange: { startOffset: 81, endOffset: 89 } } } ], @@ -115,7 +118,8 @@ module.exports = { openName: { image: "top", startOffset: 1, endOffset: 3 }, closeName: { image: "top", startOffset: 105, endOffset: 107 }, openBody: { startOffset: 0, endOffset: 4 }, - closeBody: { startOffset: 103, endOffset: 108 } + closeBody: { startOffset: 103, endOffset: 108 }, + attributesRange: { startOffset: 5, endOffset: 3 } } }, position: { startOffset: 0, endOffset: 109 } diff --git a/packages/ast/test/snapshots/invalid/bad-namespace/output.js b/packages/ast/test/snapshots/invalid/bad-namespace/output.js index 7300d09e..aafe9b1a 100644 --- a/packages/ast/test/snapshots/invalid/bad-namespace/output.js +++ b/packages/ast/test/snapshots/invalid/bad-namespace/output.js @@ -33,7 +33,8 @@ module.exports = { openName: { image: "f:name", startOffset: 25, endOffset: 30 }, closeName: { image: "f:name", startOffset: 44, endOffset: 49 }, openBody: { startOffset: 24, endOffset: 31 }, - closeBody: { startOffset: 42, endOffset: 50 } + closeBody: { startOffset: 42, endOffset: 50 }, + attributesRange: { startOffset: 32, endOffset: 30 } }, ns: "f" }, @@ -55,7 +56,8 @@ module.exports = { openName: { image: "f:width", startOffset: 57, endOffset: 63 }, closeName: { image: "f:width", startOffset: 69, endOffset: 75 }, openBody: { startOffset: 56, endOffset: 64 }, - closeBody: { startOffset: 67, endOffset: 76 } + closeBody: { startOffset: 67, endOffset: 76 }, + attributesRange: { startOffset: 65, endOffset: 63 } }, ns: "f" }, @@ -77,7 +79,8 @@ module.exports = { openName: { image: "f:length", startOffset: 83, endOffset: 90 }, closeName: { image: "f:length", startOffset: 96, endOffset: 103 }, openBody: { startOffset: 82, endOffset: 91 }, - closeBody: { startOffset: 94, endOffset: 104 } + closeBody: { startOffset: 94, endOffset: 104 }, + attributesRange: { startOffset: 92, endOffset: 90 } }, ns: "f" } @@ -109,7 +112,8 @@ module.exports = { openName: { image: "f:table", startOffset: 2, endOffset: 8 }, closeName: { image: "f:table", startOffset: 108, endOffset: 114 }, openBody: { startOffset: 1, endOffset: 18 }, - closeBody: { startOffset: 106, endOffset: 115 } + closeBody: { startOffset: 106, endOffset: 115 }, + attributesRange: { startOffset: 10, endOffset: 17 } }, ns: "f" }, diff --git a/packages/ast/test/snapshots/invalid/element-name-empty-last/output.js b/packages/ast/test/snapshots/invalid/element-name-empty-last/output.js index 7921e401..d797e9a3 100644 --- a/packages/ast/test/snapshots/invalid/element-name-empty-last/output.js +++ b/packages/ast/test/snapshots/invalid/element-name-empty-last/output.js @@ -25,7 +25,8 @@ module.exports = { openName: { image: "to", startOffset: 12, endOffset: 13 }, closeName: { image: "to", startOffset: 21, endOffset: 22 }, openBody: { startOffset: 11, endOffset: 14 }, - closeBody: { startOffset: 19, endOffset: 23 } + closeBody: { startOffset: 19, endOffset: 23 }, + attributesRange: { startOffset: 15, endOffset: 13 } } }, { @@ -46,7 +47,8 @@ module.exports = { openName: { image: "from", startOffset: 30, endOffset: 33 }, closeName: { image: "from", startOffset: 41, endOffset: 44 }, openBody: { startOffset: 29, endOffset: 34 }, - closeBody: { startOffset: 39, endOffset: 45 } + closeBody: { startOffset: 39, endOffset: 45 }, + attributesRange: { startOffset: 35, endOffset: 33 } } }, { @@ -82,7 +84,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 55, endOffset: 58 }, openBody: { startOffset: 0, endOffset: 5 }, - closeBody: { startOffset: 53, endOffset: 59 } + closeBody: { startOffset: 53, endOffset: 59 }, + attributesRange: { startOffset: 6, endOffset: 4 } } }, position: { startOffset: 0, endOffset: 60 } diff --git a/packages/ast/test/snapshots/invalid/element-name-empty-middle-comment/output.js b/packages/ast/test/snapshots/invalid/element-name-empty-middle-comment/output.js index 86e209b0..6d2cfe85 100644 --- a/packages/ast/test/snapshots/invalid/element-name-empty-middle-comment/output.js +++ b/packages/ast/test/snapshots/invalid/element-name-empty-middle-comment/output.js @@ -25,7 +25,8 @@ module.exports = { openName: { image: "to", startOffset: 12, endOffset: 13 }, closeName: { image: "to", startOffset: 21, endOffset: 22 }, openBody: { startOffset: 11, endOffset: 14 }, - closeBody: { startOffset: 19, endOffset: 23 } + closeBody: { startOffset: 19, endOffset: 23 }, + attributesRange: { startOffset: 15, endOffset: 13 } } }, { @@ -56,7 +57,8 @@ module.exports = { openName: { image: "from", startOffset: 65, endOffset: 68 }, closeName: { image: "from", startOffset: 76, endOffset: 79 }, openBody: { startOffset: 64, endOffset: 69 }, - closeBody: { startOffset: 74, endOffset: 80 } + closeBody: { startOffset: 74, endOffset: 80 }, + attributesRange: { startOffset: 70, endOffset: 68 } } } ], @@ -82,7 +84,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 84, endOffset: 87 }, openBody: { startOffset: 0, endOffset: 5 }, - closeBody: { startOffset: 82, endOffset: 88 } + closeBody: { startOffset: 82, endOffset: 88 }, + attributesRange: { startOffset: 6, endOffset: 4 } } }, position: { startOffset: 0, endOffset: 89 } diff --git a/packages/ast/test/snapshots/invalid/element-name-empty-middle/output.js b/packages/ast/test/snapshots/invalid/element-name-empty-middle/output.js index ec2bf636..02569f12 100644 --- a/packages/ast/test/snapshots/invalid/element-name-empty-middle/output.js +++ b/packages/ast/test/snapshots/invalid/element-name-empty-middle/output.js @@ -25,7 +25,8 @@ module.exports = { openName: { image: "to", startOffset: 12, endOffset: 13 }, closeName: { image: "to", startOffset: 21, endOffset: 22 }, openBody: { startOffset: 11, endOffset: 14 }, - closeBody: { startOffset: 19, endOffset: 23 } + closeBody: { startOffset: 19, endOffset: 23 }, + attributesRange: { startOffset: 15, endOffset: 13 } } }, { @@ -56,7 +57,8 @@ module.exports = { openName: { image: "from", startOffset: 36, endOffset: 39 }, closeName: { image: "from", startOffset: 47, endOffset: 50 }, openBody: { startOffset: 35, endOffset: 40 }, - closeBody: { startOffset: 45, endOffset: 51 } + closeBody: { startOffset: 45, endOffset: 51 }, + attributesRange: { startOffset: 41, endOffset: 39 } } } ], @@ -82,7 +84,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 55, endOffset: 58 }, openBody: { startOffset: 0, endOffset: 5 }, - closeBody: { startOffset: 53, endOffset: 59 } + closeBody: { startOffset: 53, endOffset: 59 }, + attributesRange: { startOffset: 6, endOffset: 4 } } }, position: { startOffset: 0, endOffset: 60 } diff --git a/packages/ast/test/snapshots/invalid/element-name-last/output.js b/packages/ast/test/snapshots/invalid/element-name-last/output.js index b6ebd28f..8d199795 100644 --- a/packages/ast/test/snapshots/invalid/element-name-last/output.js +++ b/packages/ast/test/snapshots/invalid/element-name-last/output.js @@ -25,7 +25,8 @@ module.exports = { openName: { image: "to", startOffset: 12, endOffset: 13 }, closeName: { image: "to", startOffset: 21, endOffset: 22 }, openBody: { startOffset: 11, endOffset: 14 }, - closeBody: { startOffset: 19, endOffset: 23 } + closeBody: { startOffset: 19, endOffset: 23 }, + attributesRange: { startOffset: 15, endOffset: 13 } } }, { @@ -46,7 +47,8 @@ module.exports = { openName: { image: "from", startOffset: 30, endOffset: 33 }, closeName: { image: "from", startOffset: 41, endOffset: 44 }, openBody: { startOffset: 29, endOffset: 34 }, - closeBody: { startOffset: 39, endOffset: 45 } + closeBody: { startOffset: 39, endOffset: 45 }, + attributesRange: { startOffset: 35, endOffset: 33 } } }, { @@ -57,7 +59,10 @@ module.exports = { subElements: [], textContents: [], position: { startOffset: 51, endOffset: 53 }, - syntax: { openName: { image: "ad", startOffset: 52, endOffset: 53 } } + syntax: { + openName: { image: "ad", startOffset: 52, endOffset: 53 }, + guessedAttributesRange: { startOffset: 55, endOffset: 55 } + } } ], textContents: [ @@ -82,7 +87,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 57, endOffset: 60 }, openBody: { startOffset: 0, endOffset: 5 }, - closeBody: { startOffset: 55, endOffset: 61 } + closeBody: { startOffset: 55, endOffset: 61 }, + attributesRange: { startOffset: 6, endOffset: 4 } } }, position: { startOffset: 0, endOffset: 62 } diff --git a/packages/ast/test/snapshots/invalid/element-name-middle/output.js b/packages/ast/test/snapshots/invalid/element-name-middle/output.js index b5a02406..9cec29d5 100644 --- a/packages/ast/test/snapshots/invalid/element-name-middle/output.js +++ b/packages/ast/test/snapshots/invalid/element-name-middle/output.js @@ -25,7 +25,8 @@ module.exports = { openName: { image: "to", startOffset: 12, endOffset: 13 }, closeName: { image: "to", startOffset: 21, endOffset: 22 }, openBody: { startOffset: 11, endOffset: 14 }, - closeBody: { startOffset: 19, endOffset: 23 } + closeBody: { startOffset: 19, endOffset: 23 }, + attributesRange: { startOffset: 15, endOffset: 13 } } }, { @@ -36,7 +37,10 @@ module.exports = { subElements: [], textContents: [], position: { startOffset: 29, endOffset: 31 }, - syntax: { openName: { image: "ad", startOffset: 30, endOffset: 31 } } + syntax: { + openName: { image: "ad", startOffset: 30, endOffset: 31 }, + guessedAttributesRange: { startOffset: 33, endOffset: 36 } + } }, { type: "XMLElement", @@ -56,7 +60,8 @@ module.exports = { openName: { image: "from", startOffset: 38, endOffset: 41 }, closeName: { image: "from", startOffset: 49, endOffset: 52 }, openBody: { startOffset: 37, endOffset: 42 }, - closeBody: { startOffset: 47, endOffset: 53 } + closeBody: { startOffset: 47, endOffset: 53 }, + attributesRange: { startOffset: 43, endOffset: 41 } } }, { @@ -77,7 +82,8 @@ module.exports = { openName: { image: "city", startOffset: 60, endOffset: 63 }, closeName: { image: "city", startOffset: 69, endOffset: 72 }, openBody: { startOffset: 59, endOffset: 64 }, - closeBody: { startOffset: 67, endOffset: 73 } + closeBody: { startOffset: 67, endOffset: 73 }, + attributesRange: { startOffset: 65, endOffset: 63 } } } ], @@ -108,7 +114,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 77, endOffset: 80 }, openBody: { startOffset: 0, endOffset: 5 }, - closeBody: { startOffset: 75, endOffset: 81 } + closeBody: { startOffset: 75, endOffset: 81 }, + attributesRange: { startOffset: 6, endOffset: 4 } } }, position: { startOffset: 0, endOffset: 82 } diff --git a/packages/ast/test/snapshots/valid/attributes/output.js b/packages/ast/test/snapshots/valid/attributes/output.js index 57b0759d..3c6b9125 100644 --- a/packages/ast/test/snapshots/valid/attributes/output.js +++ b/packages/ast/test/snapshots/valid/attributes/output.js @@ -34,7 +34,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 32, endOffset: 35 }, openBody: { startOffset: 0, endOffset: 29 }, - closeBody: { startOffset: 30, endOffset: 36 } + closeBody: { startOffset: 30, endOffset: 36 }, + attributesRange: { startOffset: 6, endOffset: 28 } } }, position: { startOffset: 0, endOffset: 37 } diff --git a/packages/ast/test/snapshots/valid/complex-contents/output.js b/packages/ast/test/snapshots/valid/complex-contents/output.js index 9b91862a..3838d136 100644 --- a/packages/ast/test/snapshots/valid/complex-contents/output.js +++ b/packages/ast/test/snapshots/valid/complex-contents/output.js @@ -13,7 +13,8 @@ module.exports = { openName: { image: "note", startOffset: 10, endOffset: 13 }, closeName: { image: "note", startOffset: 17, endOffset: 20 }, openBody: { startOffset: 9, endOffset: 14 }, - closeBody: { startOffset: 15, endOffset: 21 } + closeBody: { startOffset: 15, endOffset: 21 }, + attributesRange: { startOffset: 15, endOffset: 13 } } }, position: { startOffset: 0, endOffset: 22 }, diff --git a/packages/ast/test/snapshots/valid/elements-short-form/output.js b/packages/ast/test/snapshots/valid/elements-short-form/output.js index daa448a7..a81504de 100644 --- a/packages/ast/test/snapshots/valid/elements-short-form/output.js +++ b/packages/ast/test/snapshots/valid/elements-short-form/output.js @@ -25,7 +25,8 @@ module.exports = { openName: { image: "to", startOffset: 12, endOffset: 13 }, closeName: { image: "to", startOffset: 21, endOffset: 22 }, openBody: { startOffset: 11, endOffset: 14 }, - closeBody: { startOffset: 19, endOffset: 23 } + closeBody: { startOffset: 19, endOffset: 23 }, + attributesRange: { startOffset: 15, endOffset: 13 } } }, { @@ -49,7 +50,8 @@ module.exports = { position: { startOffset: 29, endOffset: 46 }, syntax: { openName: { image: "from", startOffset: 30, endOffset: 33 }, - openBody: { startOffset: 29, endOffset: 46 } + openBody: { startOffset: 29, endOffset: 46 }, + attributesRange: { startOffset: 35, endOffset: 44 } } } ], @@ -75,7 +77,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 50, endOffset: 53 }, openBody: { startOffset: 0, endOffset: 5 }, - closeBody: { startOffset: 48, endOffset: 54 } + closeBody: { startOffset: 48, endOffset: 54 }, + attributesRange: { startOffset: 6, endOffset: 4 } } }, position: { startOffset: 0, endOffset: 55 } diff --git a/packages/ast/test/snapshots/valid/elements/output.js b/packages/ast/test/snapshots/valid/elements/output.js index 15d719a8..9d72e2b0 100644 --- a/packages/ast/test/snapshots/valid/elements/output.js +++ b/packages/ast/test/snapshots/valid/elements/output.js @@ -25,7 +25,8 @@ module.exports = { openName: { image: "to", startOffset: 12, endOffset: 13 }, closeName: { image: "to", startOffset: 21, endOffset: 22 }, openBody: { startOffset: 11, endOffset: 14 }, - closeBody: { startOffset: 19, endOffset: 23 } + closeBody: { startOffset: 19, endOffset: 23 }, + attributesRange: { startOffset: 15, endOffset: 13 } } }, { @@ -46,7 +47,8 @@ module.exports = { openName: { image: "from", startOffset: 30, endOffset: 33 }, closeName: { image: "from", startOffset: 40, endOffset: 43 }, openBody: { startOffset: 29, endOffset: 34 }, - closeBody: { startOffset: 38, endOffset: 44 } + closeBody: { startOffset: 38, endOffset: 44 }, + attributesRange: { startOffset: 35, endOffset: 33 } } } ], @@ -72,7 +74,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 48, endOffset: 51 }, openBody: { startOffset: 0, endOffset: 5 }, - closeBody: { startOffset: 46, endOffset: 52 } + closeBody: { startOffset: 46, endOffset: 52 }, + attributesRange: { startOffset: 6, endOffset: 4 } } }, position: { startOffset: 0, endOffset: 53 } diff --git a/packages/ast/test/snapshots/valid/namespaces/output.js b/packages/ast/test/snapshots/valid/namespaces/output.js index 77665e33..e100a41c 100644 --- a/packages/ast/test/snapshots/valid/namespaces/output.js +++ b/packages/ast/test/snapshots/valid/namespaces/output.js @@ -60,7 +60,8 @@ module.exports = { openName: { image: "f:name", startOffset: 86, endOffset: 91 }, closeName: { image: "f:name", startOffset: 105, endOffset: 110 }, openBody: { startOffset: 85, endOffset: 92 }, - closeBody: { startOffset: 103, endOffset: 111 } + closeBody: { startOffset: 103, endOffset: 111 }, + attributesRange: { startOffset: 93, endOffset: 91 } }, ns: "f" }, @@ -85,7 +86,8 @@ module.exports = { openName: { image: "f:width", startOffset: 118, endOffset: 124 }, closeName: { image: "f:width", startOffset: 130, endOffset: 136 }, openBody: { startOffset: 117, endOffset: 125 }, - closeBody: { startOffset: 128, endOffset: 137 } + closeBody: { startOffset: 128, endOffset: 137 }, + attributesRange: { startOffset: 126, endOffset: 124 } }, ns: "f" }, @@ -110,7 +112,8 @@ module.exports = { openName: { image: "f:length", startOffset: 144, endOffset: 151 }, closeName: { image: "f:length", startOffset: 157, endOffset: 164 }, openBody: { startOffset: 143, endOffset: 152 }, - closeBody: { startOffset: 155, endOffset: 165 } + closeBody: { startOffset: 155, endOffset: 165 }, + attributesRange: { startOffset: 153, endOffset: 151 } }, ns: "f" }, @@ -143,7 +146,8 @@ module.exports = { endOffset: 208 }, openBody: { startOffset: 171, endOffset: 183 }, - closeBody: { startOffset: 196, endOffset: 209 } + closeBody: { startOffset: 196, endOffset: 209 }, + attributesRange: { startOffset: 184, endOffset: 182 } } } ], @@ -179,7 +183,8 @@ module.exports = { openName: { image: "f:table", startOffset: 2, endOffset: 8 }, closeName: { image: "f:table", startOffset: 213, endOffset: 219 }, openBody: { startOffset: 1, endOffset: 79 }, - closeBody: { startOffset: 211, endOffset: 220 } + closeBody: { startOffset: 211, endOffset: 220 }, + attributesRange: { startOffset: 10, endOffset: 78 } }, ns: "f" }, diff --git a/packages/ast/test/snapshots/valid/prolog-no-attriibs/output.js b/packages/ast/test/snapshots/valid/prolog-no-attriibs/output.js index 35300141..4e86477c 100644 --- a/packages/ast/test/snapshots/valid/prolog-no-attriibs/output.js +++ b/packages/ast/test/snapshots/valid/prolog-no-attriibs/output.js @@ -25,7 +25,8 @@ module.exports = { openName: { image: "to", startOffset: 36, endOffset: 37 }, closeName: { image: "to", startOffset: 45, endOffset: 46 }, openBody: { startOffset: 35, endOffset: 38 }, - closeBody: { startOffset: 43, endOffset: 47 } + closeBody: { startOffset: 43, endOffset: 47 }, + attributesRange: { startOffset: 39, endOffset: 37 } } }, { @@ -46,7 +47,8 @@ module.exports = { openName: { image: "from", startOffset: 64, endOffset: 67 }, closeName: { image: "from", startOffset: 74, endOffset: 77 }, openBody: { startOffset: 63, endOffset: 68 }, - closeBody: { startOffset: 72, endOffset: 78 } + closeBody: { startOffset: 72, endOffset: 78 }, + attributesRange: { startOffset: 69, endOffset: 67 } } } ], @@ -87,7 +89,8 @@ module.exports = { openName: { image: "note", startOffset: 1, endOffset: 4 }, closeName: { image: "note", startOffset: 92, endOffset: 95 }, openBody: { startOffset: 0, endOffset: 5 }, - closeBody: { startOffset: 90, endOffset: 96 } + closeBody: { startOffset: 90, endOffset: 96 }, + attributesRange: { startOffset: 6, endOffset: 4 } } }, position: { startOffset: 0, endOffset: 97 } diff --git a/packages/ast/test/snapshots/valid/prolog/output.js b/packages/ast/test/snapshots/valid/prolog/output.js index 002261c5..92e01832 100644 --- a/packages/ast/test/snapshots/valid/prolog/output.js +++ b/packages/ast/test/snapshots/valid/prolog/output.js @@ -13,7 +13,8 @@ module.exports = { openName: { image: "note", startOffset: 40, endOffset: 43 }, closeName: { image: "note", startOffset: 47, endOffset: 50 }, openBody: { startOffset: 39, endOffset: 44 }, - closeBody: { startOffset: 45, endOffset: 51 } + closeBody: { startOffset: 45, endOffset: 51 }, + attributesRange: { startOffset: 45, endOffset: 43 } } }, position: { startOffset: 0, endOffset: 52 }, diff --git a/packages/ast/test/visitor/visitor-spec.js b/packages/ast/test/visitor/visitor-spec.js index c6401d62..c54d7947 100644 --- a/packages/ast/test/visitor/visitor-spec.js +++ b/packages/ast/test/visitor/visitor-spec.js @@ -110,7 +110,7 @@ describe("The XML AST Visitor", () => { }); function getAst(text) { - const { cst } = parse(text); - const ast = buildAst(cst); + const { cst, tokenVector } = parse(text); + const ast = buildAst(cst, tokenVector); return ast; } diff --git a/packages/content-assist/lib/api.js b/packages/content-assist/lib/api.js index 8b7ad745..37fe48e8 100644 --- a/packages/content-assist/lib/api.js +++ b/packages/content-assist/lib/api.js @@ -1,5 +1,5 @@ const { parse } = require("@xml-tools/parser"); -const { buildAst } = require("@xml-tools/ast"); +const { buildAst, tokenVector } = require("@xml-tools/ast"); const { defaultsDeep, flatMap } = require("lodash"); const { computeCompletionContext } = require("./content-assist"); @@ -14,7 +14,7 @@ function getSuggestions(options) { } }); const { cst, tokenVector } = parse(actualOptions.text); - const ast = buildAst(cst); + const ast = buildAst(cst, tokenVector); const { providerType, providerArgs } = computeCompletionContext({ cst: cst, diff --git a/packages/simple-schema/README.md b/packages/simple-schema/README.md index 521beae2..cfebc468 100644 --- a/packages/simple-schema/README.md +++ b/packages/simple-schema/README.md @@ -71,8 +71,8 @@ Please see the [TypeScript Definitions](./api.d.ts) for full API details. const { validate } = require("@xml-tools/validation"); const { getSchemaValidators } = require("@xml-tools/simple-schema"); - const { cst } = parse(xmlText); - const xmlDoc = buildAst(cst); + const { cst, tokenVector } = parse(xmlText); + const xmlDoc = buildAst(cst, tokenVector); const schemaValidators = getSchemaValidators(schema); const issues = validate({ doc: xmlDoc, diff --git a/packages/simple-schema/package.json b/packages/simple-schema/package.json index 1747e2fb..aebcade2 100644 --- a/packages/simple-schema/package.json +++ b/packages/simple-schema/package.json @@ -11,7 +11,7 @@ "license": "Apache-2.0", "typings": "./api.d.ts", "dependencies": { - "@xml-tools/ast": "^0.3.0", + "@xml-tools/ast": "^0.5.0", "@xml-tools/content-assist": "^0.4.2", "lodash": "4.17.15" }, diff --git a/packages/simple-schema/test/validators/utils.js b/packages/simple-schema/test/validators/utils.js index 449563a7..694dc0aa 100644 --- a/packages/simple-schema/test/validators/utils.js +++ b/packages/simple-schema/test/validators/utils.js @@ -10,8 +10,8 @@ const { getSchemaValidators } = require("../../"); * @returns {ValidationIssue[] | *} */ function validateBySchema(xmlText, schema) { - const { cst } = parse(xmlText); - const xmlDoc = buildAst(cst); + const { cst, tokenVector } = parse(xmlText); + const xmlDoc = buildAst(cst, tokenVector); const schemaValidators = getSchemaValidators(schema); const issues = validate({ doc: xmlDoc, diff --git a/packages/validation/README.md b/packages/validation/README.md index 61ba0e0b..bde9195c 100644 --- a/packages/validation/README.md +++ b/packages/validation/README.md @@ -30,8 +30,8 @@ const xmlText = ` `; -const { cst } = parse(xmlText); -const xmlDocAst = buildAst(cst); +const { cst, tokenVector } = parse(xmlText); +const xmlDocAst = buildAst(cst, tokenVector); const issues = validate({ doc: xmlDocAst, validators: { diff --git a/packages/validation/test/validate-apis-spec.js b/packages/validation/test/validate-apis-spec.js index cf5c4937..8463dbbb 100644 --- a/packages/validation/test/validate-apis-spec.js +++ b/packages/validation/test/validate-apis-spec.js @@ -56,7 +56,7 @@ describe("The XML Validations APIs", () => { }); function xmlTextToAst(text) { - const { cst } = parse(text); - const ast = buildAst(cst); + const { cst, tokenVector } = parse(text); + const ast = buildAst(cst, tokenVector); return ast; } diff --git a/yarn.lock b/yarn.lock index c34e8a21..d30fa867 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1108,21 +1108,6 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.0.tgz#d688d574400d96c5b0114968705366f431831e1a" integrity sha512-1OzrNb4RuAzIT7wHSsgZRlMBlNsJl+do6UblR7JMW4oB7bbR+uBEYtUh7gEc/jM84GGilh68lSOokyM/zNUlBA== -"@xml-tools/ast@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@xml-tools/ast/-/ast-0.3.0.tgz#aa67175443eece11fb0c00507f07570cb2ac3078" - integrity sha512-qpOQg2mhEgOp8BNVVZgAoTQwAHgZJgneCFN7yB6NSMqXUvdpLYJQzqRo1+qe8ATqEAFqLh3eG98oFJW2isinaA== - dependencies: - "@xml-tools/parser" "^0.3.0" - lodash "4.17.15" - -"@xml-tools/parser@^0.3.0": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@xml-tools/parser/-/parser-0.3.4.tgz#709087bedca42ad11942a2632979e13b548dbfad" - integrity sha512-cw20poVHMQNN6KldoTdjybUE8uoXmlmA9OYfukcIZhhV+B2nHMoEGP9u+UDYelsmUnng80YwlRQiGHd7/5aXew== - dependencies: - chevrotain "6.5.0" - "@zkochan/cmd-shim@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz#2ab8ed81f5bb5452a85f25758eb9b8681982fd2e"