Extract some print functions in markdown (#18321)
Co-authored-by: fisker Cheung <lionkay@gmail.com>
This commit is contained in:
1 parent
864bb7c042
commit
e2ed914e02
4 files changed
+285
-239
No files matched your search
@@ -0,0 +1,113 @@
|
||||
import { hardline } from "../../document/index.js";
|
||||
import {
|
||||
INLINE_NODE_TYPES,
|
||||
INLINE_NODE_WRAPPER_TYPES,
|
||||
isPrettierIgnore,
|
||||
} from "../utils.js";
|
||||
|
||||
/**
|
||||
* @import AstPath from "../../common/ast-path.js"
|
||||
* @import {Doc} from "../../document/index.js"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {AstPath} path
|
||||
* @param {*} options
|
||||
* @param {() => Doc} print
|
||||
* @param {*} [events]
|
||||
* @return {Doc}
|
||||
*/
|
||||
function printChildren(path, options, print, events = {}) {
|
||||
const { processor = print } = events;
|
||||
|
||||
const parts = [];
|
||||
|
||||
path.each(() => {
|
||||
const result = processor(path);
|
||||
if (result !== false) {
|
||||
if (parts.length > 0 && shouldPrePrintHardline(path)) {
|
||||
parts.push(hardline);
|
||||
|
||||
if (
|
||||
shouldPrePrintDoubleHardline(path, options) ||
|
||||
shouldPrePrintTripleHardline(path)
|
||||
) {
|
||||
parts.push(hardline);
|
||||
}
|
||||
|
||||
if (shouldPrePrintTripleHardline(path)) {
|
||||
parts.push(hardline);
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(result);
|
||||
}
|
||||
}, "children");
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
function shouldPrePrintHardline({ node, parent }) {
|
||||
const isInlineNode = INLINE_NODE_TYPES.has(node.type);
|
||||
|
||||
const isInlineHTML =
|
||||
node.type === "html" && INLINE_NODE_WRAPPER_TYPES.has(parent.type);
|
||||
|
||||
return !isInlineNode && !isInlineHTML;
|
||||
}
|
||||
|
||||
const SIBLING_NODE_TYPES = new Set(["listItem", "definition"]);
|
||||
|
||||
function shouldPrePrintDoubleHardline({ node, previous, parent }, options) {
|
||||
if (
|
||||
isLooseListItem(previous, options) ||
|
||||
(node.type === "list" &&
|
||||
parent.type === "listItem" &&
|
||||
previous.type === "code")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const isSequence = previous.type === node.type;
|
||||
const isSiblingNode = isSequence && SIBLING_NODE_TYPES.has(node.type);
|
||||
const isInTightListItem =
|
||||
parent.type === "listItem" &&
|
||||
(node.type === "list" || !isLooseListItem(parent, options));
|
||||
const isPrevNodePrettierIgnore = isPrettierIgnore(previous) === "next";
|
||||
const isBlockHtmlWithoutBlankLineBetweenPrevHtml =
|
||||
node.type === "html" &&
|
||||
previous.type === "html" &&
|
||||
previous.position.end.line + 1 === node.position.start.line;
|
||||
const isHtmlDirectAfterListItem =
|
||||
node.type === "html" &&
|
||||
parent.type === "listItem" &&
|
||||
previous.type === "paragraph" &&
|
||||
previous.position.end.line + 1 === node.position.start.line;
|
||||
|
||||
return !(
|
||||
isSiblingNode ||
|
||||
isInTightListItem ||
|
||||
isPrevNodePrettierIgnore ||
|
||||
isBlockHtmlWithoutBlankLineBetweenPrevHtml ||
|
||||
isHtmlDirectAfterListItem
|
||||
);
|
||||
}
|
||||
|
||||
function shouldPrePrintTripleHardline({ node, previous }) {
|
||||
const isPrevNodeList = previous.type === "list";
|
||||
const isIndentedCode = node.type === "code" && node.isIndented;
|
||||
|
||||
return isPrevNodeList && isIndentedCode;
|
||||
}
|
||||
|
||||
function isLooseListItem(node, options) {
|
||||
return (
|
||||
node.type === "listItem" &&
|
||||
(node.spread ||
|
||||
// Check if `listItem` ends with `\n`
|
||||
// since it can't be empty, so we only need check the last character
|
||||
options.originalText.charAt(node.position.end.offset - 1) === "\n")
|
||||
);
|
||||
}
|
||||
|
||||
export { printChildren };
|
||||
@@ -0,0 +1,111 @@
|
||||
import { align } from "../../document/index.js";
|
||||
import {
|
||||
getNthListSiblingIndex,
|
||||
hasGitDiffFriendlyOrderedList,
|
||||
} from "../utils.js";
|
||||
import { printChildren } from "./children.js";
|
||||
|
||||
/**
|
||||
* @import AstPath from "../../common/ast-path.js"
|
||||
* @import {Doc} from "../../document/index.js"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {AstPath} path
|
||||
* @param {*} options
|
||||
* @param {() => Doc} print
|
||||
* @return {Doc}
|
||||
*/
|
||||
function printList(path, options, print) {
|
||||
const { node } = path;
|
||||
const nthSiblingIndex = getNthListSiblingIndex(node, path.parent);
|
||||
|
||||
const isGitDiffFriendlyOrderedList = hasGitDiffFriendlyOrderedList(
|
||||
node,
|
||||
options,
|
||||
);
|
||||
|
||||
return printChildren(path, options, print, {
|
||||
processor() {
|
||||
const prefix = getPrefix();
|
||||
const { node: childNode } = path;
|
||||
|
||||
if (
|
||||
childNode.children.length === 2 &&
|
||||
childNode.children[1].type === "html" &&
|
||||
childNode.children[0].position.start.column !==
|
||||
childNode.children[1].position.start.column
|
||||
) {
|
||||
return [prefix, printListItem(path, options, print, prefix)];
|
||||
}
|
||||
|
||||
return [
|
||||
prefix,
|
||||
align(
|
||||
" ".repeat(prefix.length),
|
||||
printListItem(path, options, print, prefix),
|
||||
),
|
||||
];
|
||||
|
||||
function getPrefix() {
|
||||
const rawPrefix = node.ordered
|
||||
? (path.isFirst
|
||||
? node.start
|
||||
: isGitDiffFriendlyOrderedList
|
||||
? 1
|
||||
: node.start + path.index) +
|
||||
(nthSiblingIndex % 2 === 0 ? ". " : ") ")
|
||||
: nthSiblingIndex % 2 === 0
|
||||
? "- "
|
||||
: "* ";
|
||||
|
||||
return (node.isAligned ||
|
||||
/* workaround for https://github.com/remarkjs/remark/issues/315 */ node.hasIndentedCodeblock) &&
|
||||
node.ordered
|
||||
? alignListPrefix(rawPrefix, options)
|
||||
: rawPrefix;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function printListItem(path, options, print, listPrefix) {
|
||||
const { node } = path;
|
||||
const prefix = node.checked === null ? "" : node.checked ? "[x] " : "[ ] ";
|
||||
return [
|
||||
prefix,
|
||||
printChildren(path, options, print, {
|
||||
processor({ node, isFirst }) {
|
||||
if (isFirst && node.type !== "list") {
|
||||
return align(" ".repeat(prefix.length), print());
|
||||
}
|
||||
|
||||
const alignment = " ".repeat(
|
||||
clamp(options.tabWidth - listPrefix.length, 0, 3), // 4+ will cause indented code block
|
||||
);
|
||||
return [alignment, align(alignment, print())];
|
||||
},
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
function alignListPrefix(prefix, options) {
|
||||
const additionalSpaces = getAdditionalSpaces();
|
||||
return (
|
||||
prefix +
|
||||
" ".repeat(
|
||||
additionalSpaces >= 4 ? 0 : additionalSpaces, // 4+ will cause indented code block
|
||||
)
|
||||
);
|
||||
|
||||
function getAdditionalSpaces() {
|
||||
const restSpaces = prefix.length % options.tabWidth;
|
||||
return restSpaces === 0 ? 0 : options.tabWidth - restSpaces;
|
||||
}
|
||||
}
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.max(min, Math.min(value, max));
|
||||
}
|
||||
|
||||
export { printList };
|
||||
@@ -24,6 +24,8 @@ import embed from "./embed.js";
|
||||
import getVisitorKeys from "./get-visitor-keys.js";
|
||||
import { locEnd, locStart } from "./loc.js";
|
||||
import { insertPragma } from "./pragma.js";
|
||||
import { printChildren } from "./print/children.js";
|
||||
import { printList } from "./print/list.js";
|
||||
import { printTable } from "./print/table.js";
|
||||
import { printParagraph } from "./print-paragraph.js";
|
||||
import preprocess from "./print-preprocess.js";
|
||||
@@ -31,10 +33,9 @@ import { printSentence } from "./print-sentence.js";
|
||||
import { printWhitespace } from "./print-whitespace.js";
|
||||
import {
|
||||
getFencedCodeBlockValue,
|
||||
hasGitDiffFriendlyOrderedList,
|
||||
INLINE_NODE_TYPES,
|
||||
INLINE_NODE_WRAPPER_TYPES,
|
||||
getNthListSiblingIndex,
|
||||
isAutolink,
|
||||
isPrettierIgnore,
|
||||
splitText,
|
||||
} from "./utils.js";
|
||||
|
||||
@@ -42,8 +43,6 @@ import {
|
||||
* @import {Doc} from "../document/index.js"
|
||||
*/
|
||||
|
||||
const SIBLING_NODE_TYPES = new Set(["listItem", "definition"]);
|
||||
|
||||
function prevOrNextWord(path) {
|
||||
const { previous, next } = path;
|
||||
const hasPrevOrNextWord =
|
||||
@@ -285,57 +284,8 @@ function genericPrint(path, options, print) {
|
||||
isHtmlComment ? hardline : markAsRoot(literalline),
|
||||
);
|
||||
}
|
||||
case "list": {
|
||||
const nthSiblingIndex = getNthListSiblingIndex(node, path.parent);
|
||||
|
||||
const isGitDiffFriendlyOrderedList = hasGitDiffFriendlyOrderedList(
|
||||
node,
|
||||
options,
|
||||
);
|
||||
|
||||
return printChildren(path, options, print, {
|
||||
processor() {
|
||||
const prefix = getPrefix();
|
||||
const { node: childNode } = path;
|
||||
|
||||
if (
|
||||
childNode.children.length === 2 &&
|
||||
childNode.children[1].type === "html" &&
|
||||
childNode.children[0].position.start.column !==
|
||||
childNode.children[1].position.start.column
|
||||
) {
|
||||
return [prefix, printListItem(path, options, print, prefix)];
|
||||
}
|
||||
|
||||
return [
|
||||
prefix,
|
||||
align(
|
||||
" ".repeat(prefix.length),
|
||||
printListItem(path, options, print, prefix),
|
||||
),
|
||||
];
|
||||
|
||||
function getPrefix() {
|
||||
const rawPrefix = node.ordered
|
||||
? (path.isFirst
|
||||
? node.start
|
||||
: isGitDiffFriendlyOrderedList
|
||||
? 1
|
||||
: node.start + path.index) +
|
||||
(nthSiblingIndex % 2 === 0 ? ". " : ") ")
|
||||
: nthSiblingIndex % 2 === 0
|
||||
? "- "
|
||||
: "* ";
|
||||
|
||||
return (node.isAligned ||
|
||||
/* workaround for https://github.com/remarkjs/remark/issues/315 */ node.hasIndentedCodeblock) &&
|
||||
node.ordered
|
||||
? alignListPrefix(rawPrefix, options)
|
||||
: rawPrefix;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
case "list":
|
||||
return printList(path, options, print);
|
||||
case "thematicBreak": {
|
||||
const { ancestors } = path;
|
||||
const counter = ancestors.findIndex((node) => node.type === "list");
|
||||
@@ -457,65 +407,6 @@ function genericPrint(path, options, print) {
|
||||
}
|
||||
}
|
||||
|
||||
function printListItem(path, options, print, listPrefix) {
|
||||
const { node } = path;
|
||||
const prefix = node.checked === null ? "" : node.checked ? "[x] " : "[ ] ";
|
||||
return [
|
||||
prefix,
|
||||
printChildren(path, options, print, {
|
||||
processor({ node, isFirst }) {
|
||||
if (isFirst && node.type !== "list") {
|
||||
return align(" ".repeat(prefix.length), print());
|
||||
}
|
||||
|
||||
const alignment = " ".repeat(
|
||||
clamp(options.tabWidth - listPrefix.length, 0, 3), // 4+ will cause indented code block
|
||||
);
|
||||
return [alignment, align(alignment, print())];
|
||||
},
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
function alignListPrefix(prefix, options) {
|
||||
const additionalSpaces = getAdditionalSpaces();
|
||||
return (
|
||||
prefix +
|
||||
" ".repeat(
|
||||
additionalSpaces >= 4 ? 0 : additionalSpaces, // 4+ will cause indented code block
|
||||
)
|
||||
);
|
||||
|
||||
function getAdditionalSpaces() {
|
||||
const restSpaces = prefix.length % options.tabWidth;
|
||||
return restSpaces === 0 ? 0 : options.tabWidth - restSpaces;
|
||||
}
|
||||
}
|
||||
|
||||
function getNthListSiblingIndex(node, parentNode) {
|
||||
return getNthSiblingIndex(
|
||||
node,
|
||||
parentNode,
|
||||
(siblingNode) => siblingNode.ordered === node.ordered,
|
||||
);
|
||||
}
|
||||
|
||||
function getNthSiblingIndex(node, parentNode, condition) {
|
||||
let index = -1;
|
||||
|
||||
for (const childNode of parentNode.children) {
|
||||
if (childNode.type === node.type && condition(childNode)) {
|
||||
index++;
|
||||
} else {
|
||||
index = -1;
|
||||
}
|
||||
|
||||
if (childNode === node) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function printRoot(path, options, print) {
|
||||
/** @typedef {{ index: number, offset: number }} IgnorePosition */
|
||||
/** @type {Array<{start: IgnorePosition, end: IgnorePosition}>} */
|
||||
@@ -578,36 +469,6 @@ function printRoot(path, options, print) {
|
||||
});
|
||||
}
|
||||
|
||||
function printChildren(path, options, print, events = {}) {
|
||||
const { processor = print } = events;
|
||||
|
||||
const parts = [];
|
||||
|
||||
path.each(() => {
|
||||
const result = processor(path);
|
||||
if (result !== false) {
|
||||
if (parts.length > 0 && shouldPrePrintHardline(path)) {
|
||||
parts.push(hardline);
|
||||
|
||||
if (
|
||||
shouldPrePrintDoubleHardline(path, options) ||
|
||||
shouldPrePrintTripleHardline(path)
|
||||
) {
|
||||
parts.push(hardline);
|
||||
}
|
||||
|
||||
if (shouldPrePrintTripleHardline(path)) {
|
||||
parts.push(hardline);
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(result);
|
||||
}
|
||||
}, "children");
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
function printIgnoreComment(node) {
|
||||
if (node.type === "html") {
|
||||
return node.value;
|
||||
@@ -623,96 +484,6 @@ function printIgnoreComment(node) {
|
||||
}
|
||||
}
|
||||
|
||||
/** @return {false | 'next' | 'start' | 'end'} */
|
||||
function isPrettierIgnore(node) {
|
||||
let match;
|
||||
|
||||
if (node.type === "html") {
|
||||
match = node.value.match(
|
||||
/^<!--\s*prettier-ignore(?:-(start|end))?\s*-->$/u,
|
||||
);
|
||||
} else {
|
||||
let comment;
|
||||
|
||||
if (node.type === "esComment") {
|
||||
comment = node;
|
||||
} else if (
|
||||
node.type === "paragraph" &&
|
||||
node.children.length === 1 &&
|
||||
node.children[0].type === "esComment"
|
||||
) {
|
||||
comment = node.children[0];
|
||||
}
|
||||
|
||||
if (comment) {
|
||||
match = comment.value.match(/^prettier-ignore(?:-(start|end))?$/u);
|
||||
}
|
||||
}
|
||||
|
||||
return match ? match[1] || "next" : false;
|
||||
}
|
||||
|
||||
function shouldPrePrintHardline({ node, parent }) {
|
||||
const isInlineNode = INLINE_NODE_TYPES.has(node.type);
|
||||
|
||||
const isInlineHTML =
|
||||
node.type === "html" && INLINE_NODE_WRAPPER_TYPES.has(parent.type);
|
||||
|
||||
return !isInlineNode && !isInlineHTML;
|
||||
}
|
||||
|
||||
function isLooseListItem(node, options) {
|
||||
return (
|
||||
node.type === "listItem" &&
|
||||
(node.spread ||
|
||||
// Check if `listItem` ends with `\n`
|
||||
// since it can't be empty, so we only need check the last character
|
||||
options.originalText.charAt(node.position.end.offset - 1) === "\n")
|
||||
);
|
||||
}
|
||||
|
||||
function shouldPrePrintDoubleHardline({ node, previous, parent }, options) {
|
||||
if (
|
||||
isLooseListItem(previous, options) ||
|
||||
(node.type === "list" &&
|
||||
parent.type === "listItem" &&
|
||||
previous.type === "code")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const isSequence = previous.type === node.type;
|
||||
const isSiblingNode = isSequence && SIBLING_NODE_TYPES.has(node.type);
|
||||
const isInTightListItem =
|
||||
parent.type === "listItem" &&
|
||||
(node.type === "list" || !isLooseListItem(parent, options));
|
||||
const isPrevNodePrettierIgnore = isPrettierIgnore(previous) === "next";
|
||||
const isBlockHtmlWithoutBlankLineBetweenPrevHtml =
|
||||
node.type === "html" &&
|
||||
previous.type === "html" &&
|
||||
previous.position.end.line + 1 === node.position.start.line;
|
||||
const isHtmlDirectAfterListItem =
|
||||
node.type === "html" &&
|
||||
parent.type === "listItem" &&
|
||||
previous.type === "paragraph" &&
|
||||
previous.position.end.line + 1 === node.position.start.line;
|
||||
|
||||
return !(
|
||||
isSiblingNode ||
|
||||
isInTightListItem ||
|
||||
isPrevNodePrettierIgnore ||
|
||||
isBlockHtmlWithoutBlankLineBetweenPrevHtml ||
|
||||
isHtmlDirectAfterListItem
|
||||
);
|
||||
}
|
||||
|
||||
function shouldPrePrintTripleHardline({ node, previous }) {
|
||||
const isPrevNodeList = previous.type === "list";
|
||||
const isIndentedCode = node.type === "code" && node.isIndented;
|
||||
|
||||
return isPrevNodeList && isIndentedCode;
|
||||
}
|
||||
|
||||
function shouldRemainTheSameContent(path) {
|
||||
const node = path.findAncestor(
|
||||
(node) => node.type === "linkReference" || node.type === "imageReference",
|
||||
@@ -770,10 +541,6 @@ function printTitle(title, options, printSpace = true) {
|
||||
return `${quote}${title}${quote}`;
|
||||
}
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.max(min, Math.min(value, max));
|
||||
}
|
||||
|
||||
function hasPrettierIgnore(path) {
|
||||
return path.index > 0 && isPrettierIgnore(path.previous) === "next";
|
||||
}
|
||||
|
||||
@@ -252,13 +252,68 @@ function isAutolink(node) {
|
||||
return locStart(node) === locStart(child) && locEnd(node) === locEnd(child);
|
||||
}
|
||||
|
||||
/** @return {false | 'next' | 'start' | 'end'} */
|
||||
function isPrettierIgnore(node) {
|
||||
let match;
|
||||
|
||||
if (node.type === "html") {
|
||||
match = node.value.match(
|
||||
/^<!--\s*prettier-ignore(?:-(start|end))?\s*-->$/u,
|
||||
);
|
||||
} else {
|
||||
let comment;
|
||||
|
||||
if (node.type === "esComment") {
|
||||
comment = node;
|
||||
} else if (
|
||||
node.type === "paragraph" &&
|
||||
node.children.length === 1 &&
|
||||
node.children[0].type === "esComment"
|
||||
) {
|
||||
comment = node.children[0];
|
||||
}
|
||||
|
||||
if (comment) {
|
||||
match = comment.value.match(/^prettier-ignore(?:-(start|end))?$/u);
|
||||
}
|
||||
}
|
||||
|
||||
return match ? match[1] || "next" : false;
|
||||
}
|
||||
|
||||
function getNthListSiblingIndex(node, parentNode) {
|
||||
return getNthSiblingIndex(
|
||||
node,
|
||||
parentNode,
|
||||
(siblingNode) => siblingNode.ordered === node.ordered,
|
||||
);
|
||||
|
||||
function getNthSiblingIndex(node, parentNode, condition) {
|
||||
let index = -1;
|
||||
|
||||
for (const childNode of parentNode.children) {
|
||||
if (childNode.type === node.type && condition(childNode)) {
|
||||
index++;
|
||||
} else {
|
||||
index = -1;
|
||||
}
|
||||
|
||||
if (childNode === node) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getFencedCodeBlockValue,
|
||||
getNthListSiblingIndex,
|
||||
getOrderedListItemInfo,
|
||||
hasGitDiffFriendlyOrderedList,
|
||||
INLINE_NODE_TYPES,
|
||||
INLINE_NODE_WRAPPER_TYPES,
|
||||
isAutolink,
|
||||
isPrettierIgnore,
|
||||
KIND_CJ_LETTER,
|
||||
KIND_CJK_PUNCTUATION,
|
||||
KIND_K_LETTER,
|
||||
|
||||
Reference in new issue
Block a user