diff --git a/src/common/ast-path.js b/src/common/ast-path.js index 851542a3b..760ab0a96 100644 --- a/src/common/ast-path.js +++ b/src/common/ast-path.js @@ -1,35 +1,73 @@ import getLast from "../utils/get-last.js"; - -function getNodeHelper(path, count) { - const stackIndex = getNodeStackIndexHelper(path.stack, count); - return stackIndex === -1 ? null : path.stack[stackIndex]; -} - -function getNodeStackIndexHelper(stack, count) { - for (let i = stack.length - 1; i >= 0; i -= 2) { - const value = stack[i]; - if (value && !Array.isArray(value) && --count < 0) { - return i; - } - } - return -1; -} +import { getPenultimate } from "./util.js"; class AstPath { constructor(value) { this.stack = [value]; } + get key() { + const { stack, siblings } = this; + return stack[stack.length - (siblings === null ? 4 : 2)] ?? null; + } + + get index() { + return this.siblings === null ? null : getPenultimate(this.stack); + } + + get node() { + return getLast(this.stack); + } + + get parent() { + return this.getNode(1); + } + + get grandparent() { + return this.getNode(2); + } + + get isInArray() { + return this.siblings !== null; + } + + get siblings() { + const { stack } = this; + const maybeArray = stack[stack.length - 3]; + return Array.isArray(maybeArray) ? maybeArray : null; + } + + get next() { + return this.siblings[this.index + 1]; + } + + get previous() { + return this.siblings[this.index - 1]; + } + + get isFirst() { + return this.index === 0; + } + + get isLast() { + const { siblings, index } = this; + return siblings !== null && index === siblings.length - 1; + } + + get isRoot() { + return this.stack.length === 1; + } + // The name of the current property is always the penultimate element of - // this.stack, and always a String. + // this.stack, and always a string/number/symbol. getName() { const { stack } = this; const { length } = stack; if (length > 1) { return stack[length - 2]; } - // Since the name is always a string, null is a safe sentinel value to - // return if we do not know the name of the (root) value. + // Since the name is a string/number/symbol, null is a safe sentinel value + // to return if we do not know the name of the (root) value. /* istanbul ignore next */ return null; } @@ -41,11 +79,22 @@ class AstPath { } getNode(count = 0) { - return getNodeHelper(this, count); + const stackIndex = this.#getNodeStackIndex(count); + return stackIndex === -1 ? null : this.stack[stackIndex]; } getParentNode(count = 0) { - return getNodeHelper(this, count + 1); + return this.getNode(count + 1); + } + + #getNodeStackIndex(count) { + const { stack } = this; + for (let i = stack.length - 1; i >= 0; i -= 2) { + if (!Array.isArray(stack[i]) && --count < 0) { + return i; + } + } + return -1; } // Temporarily push properties named by string arguments given after the @@ -70,7 +119,7 @@ class AstPath { } callParent(callback, count = 0) { - const stackIndex = getNodeStackIndexHelper(this.stack, count + 1); + const stackIndex = this.#getNodeStackIndex(count + 1); const parentValues = this.stack.splice(stackIndex + 1); try { return callback(this); diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index d3a644c3e..615d3147f 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -55,7 +55,7 @@ const SIBLING_NODE_TYPES = new Set([ ]); function genericPrint(path, options, print) { - const node = path.getValue(); + const { node } = path; if (shouldRemainTheSameContent(path)) { return splitText( @@ -137,13 +137,11 @@ function genericPrint(path, options, print) { return escapedValue; } case "whitespace": { - const parentNode = path.getParentNode(); - const index = parentNode.children.indexOf(node); - const nextNode = parentNode.children[index + 1]; + const { next } = path; const proseWrap = // leading char that may cause different syntax - nextNode && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/.test(nextNode.value) + next && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/.test(next.value) ? "never" : options.proseWrap; @@ -154,21 +152,14 @@ function genericPrint(path, options, print) { if (isAutolink(node.children[0])) { style = options.originalText[node.position.start.offset]; } else { - const parentNode = path.getParentNode(); - const index = parentNode.children.indexOf(node); - const prevNode = parentNode.children[index - 1]; - const nextNode = parentNode.children[index + 1]; + const { previous, next } = path; const hasPrevOrNextWord = // `1*2*3` is considered emphasis but `1_2_3` is not - (prevNode && - prevNode.type === "sentence" && - prevNode.children.length > 0 && - getLast(prevNode.children).type === "word" && - !getLast(prevNode.children).hasTrailingPunctuation) || - (nextNode && - nextNode.type === "sentence" && - nextNode.children.length > 0 && - nextNode.children[0].type === "word" && - !nextNode.children[0].hasLeadingPunctuation); + (previous?.type === "sentence" && + getLast(previous.children)?.type === "word" && + !getLast(previous.children).hasTrailingPunctuation) || + (next?.type === "sentence" && + next.children[0]?.type === "word" && + !next.children[0].hasLeadingPunctuation); style = hasPrevOrNextWord || getAncestorNode(path, "emphasis") ? "*" : "_"; } @@ -269,9 +260,9 @@ function genericPrint(path, options, print) { ]; } case "html": { - const parentNode = path.getParentNode(); + const { parent } = path; const value = - parentNode.type === "root" && getLast(parentNode.children) === node + parent.type === "root" && getLast(parent.children) === node ? node.value.trimEnd() : node.value; const isHtmlComment = /^$/s.test(value); @@ -283,10 +274,7 @@ function genericPrint(path, options, print) { ); } case "list": { - const nthSiblingIndex = getNthListSiblingIndex( - node, - path.getParentNode() - ); + const nthSiblingIndex = getNthListSiblingIndex(node, path.parent); const isGitDiffFriendlyOrderedList = hasGitDiffFriendlyOrderedList( node, @@ -296,7 +284,7 @@ function genericPrint(path, options, print) { return printChildren(path, options, print, { processor: (childPath, index) => { const prefix = getPrefix(); - const childNode = childPath.getValue(); + const childNode = childPath.node; if ( childNode.children.length === 2 && @@ -392,7 +380,6 @@ function genericPrint(path, options, print) { case "footnoteReference": return ["[^", node.identifier, "]"]; case "footnoteDefinition": { - const nextNode = path.getParentNode().children[path.getName() + 1]; const shouldInlineFootnote = node.children.length === 1 && node.children[0].type === "paragraph" && @@ -414,9 +401,7 @@ function genericPrint(path, options, print) { index === 0 ? group([softline, print()]) : print(), }) ), - nextNode && nextNode.type === "footnoteDefinition" - ? softline - : "", + path.next?.type === "footnoteDefinition" ? softline : "", ]), ]; } @@ -464,13 +449,13 @@ function genericPrint(path, options, print) { } function printListItem(path, options, print, listPrefix) { - const node = path.getValue(); + const { node } = path; const prefix = node.checked === null ? "" : node.checked ? "[x] " : "[ ] "; return [ prefix, printChildren(path, options, print, { processor: (childPath, index) => { - if (index === 0 && childPath.getValue().type !== "list") { + if (index === 0 && childPath.node.type !== "list") { return align(" ".repeat(prefix.length), print()); } @@ -560,7 +545,7 @@ function printLine(path, value, options) { } function printTable(path, options, print) { - const node = path.getValue(); + const { node } = path; const columnMaxWidths = []; // { [rowIndex: number]: { [columnIndex: number]: {text: string, width: number} } } @@ -644,7 +629,7 @@ function printRoot(path, options, print) { /** @type {IgnorePosition | null} */ let ignoreStart = null; - const { children } = path.getValue(); + const { children } = path.node; for (const [index, childNode] of children.entries()) { switch (isPrettierIgnore(childNode)) { case "start": @@ -702,13 +687,13 @@ function printChildren(path, options, print, events = {}) { const { postprocessor } = events; const processor = events.processor || (() => print()); - const node = path.getValue(); + const { node } = path; const parts = []; let lastChildNode; path.each((childPath, index) => { - const childNode = childPath.getValue(); + const childNode = childPath.node; const result = processor(childPath, index); if (result !== false) { @@ -833,7 +818,7 @@ function shouldPrePrintDoubleHardline(node, data) { } function shouldPrePrintTripleHardline(node, data) { - const isPrevNodeList = data.prevNode && data.prevNode.type === "list"; + const isPrevNodeList = data.prevNode?.type === "list"; const isIndentedCode = node.type === "code" && node.isIndented; return isPrevNodeList && isIndentedCode; @@ -904,14 +889,7 @@ function clamp(value, min, max) { } function hasPrettierIgnore(path) { - const index = Number(path.getName()); - - if (index === 0) { - return false; - } - - const prevNode = path.getParentNode().children[index - 1]; - return isPrettierIgnore(prevNode) === "next"; + return path.index > 0 && isPrettierIgnore(path.previous) === "next"; } const printer = {