diff --git a/src/__snapshots__/attach.test.ts.snap b/src/__snapshots__/attach.test.ts.snap index 86dad92e..15ee7e11 100644 --- a/src/__snapshots__/attach.test.ts.snap +++ b/src/__snapshots__/attach.test.ts.snap @@ -203,11 +203,11 @@ comment (24:5 ~ 24:11) | ^^^^^^ 25 | ··#·Flow·style¶ 26 | ··Numbers:·!!omap·[·one:·1,·two:·2,·three·:·3·]¶ -mappingItem (26:3 ~ 26:48) - - - - +sequence (21:5 ~ 23:57) + + + + `; exports[`"\\n---\\n# Collection Types ################################# 8`] = ` @@ -220,7 +220,6 @@ comment (25:3 ~ 25:15) 27 | ¶ mappingItem (26:3 ~ 26:48) - `; @@ -1294,6 +1293,91 @@ mappingItem (204:1 ~ 206:14) `; +exports[`"\\n---\\n- Mark McGwire\\n- Sammy Sosa\\n- Ken Griffey\\n\\n# Tea 1`] = ` +comment (7:1 ~ 7:15) + 5 | -·Ken·Griffey¶ + 6 | ¶ + 7 | #·Team·ranking¶ + | ^^^^^^^^^^^^^^ + 8 | ---¶ + 9 | -·Chicago·Cubs¶ +documentBody (3:1 ~ 7:15) + + + +`; + +exports[`"\\n---\\n- Mark McGwire\\n- Sammy Sosa\\n- Ken Griffey\\n\\n# Tea 2`] = ` +comment (7:1 ~ 7:15) + 5 | -·Ken·Griffey¶ + 6 | ¶ + 7 | #·Team·ranking¶ + | ^^^^^^^^^^^^^^ + 8 | ...¶ + 9 | -·Chicago·Cubs¶ +documentBody (3:1 ~ 8:4) + + + +`; + +exports[`"\\na:\\n b:\\n #b\\n #a\\n\\nA:\\n B:\\n #A\\n #A\\n" 1`] = ` +comment (4:4 ~ 4:6) + 2 | a:¶ + 3 | ··b:¶ + 4 | ···#b¶ + | ^^ + 5 | ·#a¶ + 6 | ¶ +mappingValue (3:4 ~ 3:5) + + + +`; + +exports[`"\\na:\\n b:\\n #b\\n #a\\n\\nA:\\n B:\\n #A\\n #A\\n" 2`] = ` +comment (5:2 ~ 5:4) + 3 | ··b:¶ + 4 | ···#b¶ + 5 | ·#a¶ + | ^^ + 6 | ¶ + 7 | A:¶ +mappingValue (2:2 ~ 4:6) + + + +`; + +exports[`"\\na:\\n b:\\n #b\\n #a\\n\\nA:\\n B:\\n #A\\n #A\\n" 3`] = ` +comment (9:2 ~ 9:4) + 7 | A:¶ + 8 | ··B:¶ + 9 | ·#A¶ + | ^^ +10 | ···#A¶ +11 | ¶ +mappingValue (7:2 ~ 8:5) + + + + +`; + +exports[`"\\na:\\n b:\\n #b\\n #a\\n\\nA:\\n B:\\n #A\\n #A\\n" 4`] = ` +comment (10:4 ~ 10:6) + 8 | ··B:¶ + 9 | ·#A¶ +10 | ···#A¶ + | ^^ +11 | ¶ +mappingValue (7:2 ~ 8:5) + + + + +`; + exports[`"\\naaa\\n# 123\\n---\\n# 789\\nbbb\\n" 1`] = ` comment (3:1 ~ 3:6) 1 | ¶ @@ -1419,6 +1503,19 @@ sequenceItem (3:3 ~ 3:8) `; +exports[`"\\nparent:\\n one: 1\\n # two: 2\\n" 1`] = ` +comment (4:3 ~ 4:11) +2 | parent:¶ +3 | ··one:·1¶ +4 | ··#·two:·2¶ + | ^^^^^^^^ +5 | ¶ +mappingValue (2:7 ~ 3:9) + + + +`; + exports[`"a:\\n 123\\n # impicitMappginValue\\n" 1`] = ` comment (3:3 ~ 3:24) 1 | a:¶ diff --git a/src/attach.test.ts b/src/attach.test.ts index fb418537..2c0f8d84 100644 --- a/src/attach.test.ts +++ b/src/attach.test.ts @@ -266,6 +266,44 @@ d: # 3 # 4 # 5 +`, + ` +parent: + one: 1 + # two: 2 +`, + ` +a: + b: + #b + #a + +A: + B: + #A + #A +`, + ` +--- +- Mark McGwire +- Sammy Sosa +- Ken Griffey + +# Team ranking +--- +- Chicago Cubs +- St Louis Cardinals +`, + ` +--- +- Mark McGwire +- Sammy Sosa +- Ken Griffey + +# Team ranking +... +- Chicago Cubs +- St Louis Cardinals `, ]; diff --git a/src/attach.ts b/src/attach.ts index 644f9c15..50bfc3ec 100644 --- a/src/attach.ts +++ b/src/attach.ts @@ -5,15 +5,11 @@ import { Document, EndCommentAttachable, MappingItem, - MappingKey, - MappingValue, Root, - Sequence, - SequenceItem, YamlUnistNode, } from "./types"; import { - defineCommentParent, + defineParent, getLast, getStartPoint, isBlockValue, @@ -22,28 +18,14 @@ import { } from "./utils"; type NodeTable = Array<{ - leadingNode: null | Extract; - trailingNode: null | Extract; - collectionNode: null | { - node: SequenceItem | MappingKey | MappingValue; - column: number; - cases: NodeTableCollectionNodeCase[]; - }; + leadingAttachableNode: null | Extract; + trailingAttachableNode: null | Extract; + trailingNode: null | YamlUnistNode; + comment: null | Comment; }>; -interface NodeTableCollectionNodeCase { - mode: "gt" | "gte"; - attachee: Extract; -} - export function attachComments(root: Root, context: Context): void { - const nodeTable: NodeTable = context.text.split("\n").map(() => ({ - leadingNode: null, - trailingNode: null, - collectionNode: null, - })); - - initNodeTable(root, nodeTable, context); + const nodeTable = createNodeTable(root, context); const restDocuments = root.children.slice(); context.comments @@ -62,26 +44,44 @@ export function attachComments(root: Root, context: Context): void { updateEndPoints(root); } +function createNodeTable(root: Root, context: Context) { + const nodeTable: NodeTable = context.text.split("\n").map(() => ({ + leadingAttachableNode: null, + trailingAttachableNode: null, + trailingNode: null, + comment: null, + })); + + for (const comment of context.comments) { + nodeTable[comment.position.start.line - 1].comment = comment; + } + + initNodeTable(root, nodeTable, context); + + return nodeTable; +} + function initNodeTable( node: YamlUnistNode, nodeTable: NodeTable, context: Context, - parentStack: YamlUnistNode[] = [], ): void { if ("leadingComments" in node) { const start = getStartPoint(node); - const { end } = node.position; - - const currentStartNode = nodeTable[start.line - 1].leadingNode; - const currentEndNode = nodeTable[end.line - 1].trailingNode; + const currentStartNode = nodeTable[start.line - 1].leadingAttachableNode; if ( node.type !== "document" && (!currentStartNode || start.column < currentStartNode.position.start.column) ) { - nodeTable[start.line - 1].leadingNode = node; + nodeTable[start.line - 1].leadingAttachableNode = node; } + } + + if ("trailingComments" in node) { + const { end } = node.position; + const currentEndNode = nodeTable[end.line - 1].trailingAttachableNode; if ( !( @@ -93,50 +93,33 @@ function initNodeTable( ) && (!currentEndNode || end.column >= currentEndNode.position.end.column) ) { - nodeTable[end.line - 1].trailingNode = node; + nodeTable[end.line - 1].trailingAttachableNode = node; } } - const parent = getLast(parentStack); - if ( - node.type === "sequenceItem" || - ((node.type === "mappingValue" || - (node.type === "mappingKey" && - isExplicitMappingItem(parent as MappingItem))) && - !isBlockValue(node.children[0])) + node.type !== "null" && + node.type !== "root" && + node.type !== "document" && + node.type !== "documentHead" && + node.type !== "documentBody" ) { - const column = - node.type === "mappingValue" && - !isExplicitMappingItem(parent as MappingItem) - ? parent!.position.start.column - : node.position.start.column; - - const cases: NodeTableCollectionNodeCase[] = [ - { mode: "gt", attachee: node }, - ]; - - if (node.type === "sequenceItem") { - const parentParent = parentStack[parentStack.length - 2]; - if (parentParent.type !== "documentBody") { - const sequence = parent as Sequence; - if (node === getLast(sequence.children)) { - cases.push({ mode: "gte", attachee: sequence }); - } + const { start, end } = node.position; + const lines = [end.line].concat(start.line === end.line ? [] : start.line); + + for (const line of lines) { + const currentEndNode = nodeTable[line - 1].trailingNode; + if (!currentEndNode || end.column >= currentEndNode.position.end.column) { + nodeTable[line - 1].trailingNode = node; } } - - nodeTable[node.position.start.line - 1].collectionNode = { - node, - column, - cases, - }; } if ("children" in node) { - (node.children as YamlUnistNode[]).forEach(child => - initNodeTable(child, nodeTable, context, parentStack.concat(node)), - ); + (node.children as YamlUnistNode[]).forEach(child => { + defineParent(child, node); + initNodeTable(child, nodeTable, context); + }); } } @@ -147,49 +130,103 @@ function attachComment( ) { const commentLine = comment.position.start.line; - const trailingNode = nodeTable[commentLine - 1].trailingNode; + const { trailingAttachableNode } = nodeTable[commentLine - 1]; if ( - trailingNode !== null && - trailingNode.type !== "blockFolded" && - trailingNode.type !== "blockLiteral" + trailingAttachableNode !== null && + trailingAttachableNode.type !== "blockFolded" && + trailingAttachableNode.type !== "blockLiteral" ) { - defineCommentParent(comment, trailingNode); - trailingNode.trailingComments.push(comment); + defineParent(comment, trailingAttachableNode); + trailingAttachableNode.trailingComments.push(comment); return; } for (let line = commentLine; line >= document.position.start.line; line--) { - const { collectionNode } = nodeTable[line - 1]; - - if (collectionNode === null) { - continue; + const { trailingNode } = nodeTable[line - 1]; + + let currentNode: YamlUnistNode; + + if (trailingNode === null) { + /** + * a: + * b: + * #b + * #a + * + * a: + * b: + * #a + * #a + */ + if (line !== commentLine && nodeTable[line - 1].comment !== null) { + currentNode = nodeTable[line - 1].comment!.parent!; + } else { + continue; + } + } else { + currentNode = trailingNode; } - const { cases, node, column } = collectionNode; - - for (const { mode, attachee } of cases) { - if ( - node.position.end.offset <= comment.position.start.offset && - column + (mode === "gt" ? 1 : 0) <= comment.position.start.column - ) { - defineCommentParent(comment, attachee); - attachee.endComments.push(comment); + while (true) { + if (ownEndComment(currentNode, comment)) { + defineParent(comment, currentNode); + currentNode.endComments.push(comment); return; } + + if (currentNode.parent === undefined) { + break; + } + + currentNode = currentNode.parent; } break; } for (let line = commentLine + 1; line <= document.position.end.line; line++) { - const leadingNode = nodeTable[line - 1].leadingNode; - if (leadingNode !== null) { - defineCommentParent(comment, leadingNode); - leadingNode.leadingComments.push(comment); + const { leadingAttachableNode } = nodeTable[line - 1]; + if (leadingAttachableNode !== null) { + defineParent(comment, leadingAttachableNode); + leadingAttachableNode.leadingComments.push(comment); return; } } - defineCommentParent(comment, document.children[1]); + defineParent(comment, document.children[1]); document.children[1].children.push(comment); } + +function ownEndComment( + node: YamlUnistNode, + comment: Comment, +): node is Extract { + if (comment.position.end.offset < node.position.end.offset) { + return false; + } + + switch (node.type) { + case "sequence": + return ( + node.parent!.type !== "documentBody" && + comment.position.start.column >= node.position.start.column && + comment.position.start.offset > + getLast(node.children)!.position.end.offset + ); + case "sequenceItem": + return comment.position.start.column > node.position.start.column; + case "mappingValue": + return ( + comment.position.start.column > node.parent!.position.start.column && + !isBlockValue(node.children[0]) + ); + case "mappingKey": + return ( + comment.position.start.column > node.parent!.position.start.column && + !isBlockValue(node.children[0]) && + isExplicitMappingItem(node.parent as MappingItem) + ); + default: + return false; + } +} diff --git a/src/transform.ts b/src/transform.ts index b05c5163..52e05fe6 100644 --- a/src/transform.ts +++ b/src/transform.ts @@ -41,7 +41,7 @@ import { SequenceItem, YamlUnistNode, } from "./types"; -import { defineCommentParent } from "./utils"; +import { defineParent } from "./utils"; export type YamlNode = | null @@ -148,7 +148,7 @@ export function transformNode(node: YamlNode, context: Context): YamlUnistNode { newStartOffset <= start && transformedNode.position.start.offset >= end ) { - defineCommentParent(comment, transformedNode); + defineParent(comment, transformedNode); transformedNode.middleComments.push(comment); } else if ( (transformedNode.type === "blockFolded" || @@ -156,7 +156,7 @@ export function transformNode(node: YamlNode, context: Context): YamlUnistNode { (transformedNode.position.start.offset < start && transformedNode.position.end.offset > end) ) { - defineCommentParent(comment, transformedNode); + defineParent(comment, transformedNode); transformedNode.trailingComments.push(comment); } context.comments.push(comment); diff --git a/src/transforms/document.ts b/src/transforms/document.ts index e1c60169..6e4265f2 100644 --- a/src/transforms/document.ts +++ b/src/transforms/document.ts @@ -1,11 +1,7 @@ import assert = require("assert"); import { Context } from "../transform"; import { Document, DocumentHead, Position } from "../types"; -import { - createCommentAttachableNode, - defineCommentParent, - getLast, -} from "../utils"; +import { createCommentAttachableNode, defineParent, getLast } from "../utils"; export function transformDocument( document: yaml.Document, @@ -91,7 +87,7 @@ export function transformDocument( directive => { if (directive.type === "comment") { context.comments.push(directive); - defineCommentParent(directive, documentHead); + defineParent(directive, documentHead); } return directive; }, diff --git a/src/types.ts b/src/types.ts index 6f84d8f9..2e30a1a6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -15,6 +15,8 @@ export interface Position { export interface Node { type: string; position: Position; + /** @internal non-enumerable */ + parent?: YamlUnistNode; } export interface Parent extends Node { @@ -100,8 +102,6 @@ export interface Null extends Node { export interface Comment extends Node { type: "comment"; value: string; - /** @internal non-enumerable */ - parent?: Extract; } export interface Anchor extends Node { diff --git a/src/utils.ts b/src/utils.ts index 879c32d3..ed746c01 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -26,8 +26,8 @@ export function getLast(array: T[]) { return array[array.length - 1] as T | undefined; } -export function defineCommentParent(comment: Comment, parent: YamlUnistNode) { - Object.defineProperty(comment, "parent", { +export function defineParent(node: YamlUnistNode, parent: YamlUnistNode) { + Object.defineProperty(node, "parent", { value: parent, enumerable: false, }); diff --git a/tsconfig.build.json b/tsconfig.build.json index b48eaafd..101ad79f 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -3,7 +3,6 @@ "include": ["src/index.ts"], "compilerOptions": { "rootDir": "src", - "outDir": "lib", - "inlineSourceMap": false + "outDir": "lib" } } diff --git a/tsconfig.json b/tsconfig.json index 89ea5bd7..78562f60 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,8 +6,7 @@ "declaration": true, "importHelpers": true, "noUnusedLocals": true, - "noUnusedParameters": true, - "inlineSourceMap": true + "noUnusedParameters": true }, "include": ["src/**/*.ts", "tests/**/*.ts"] }