Files
test-repo/src/language-markdown/printer-markdown.js
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

925 lines
26 KiB
JavaScript
Raw Normal View History

2017-10-11 17:46:44 -05:00
"use strict";
2020-07-05 00:34:35 +08:00
const {
getLast,
getMinNotPresentContinuousCount,
getMaxContinuousCount,
getStringWidth,
2020-12-17 14:46:59 +08:00
isNonEmptyArray,
2020-07-05 00:34:35 +08:00
} = require("../common/util");
2018-05-27 21:36:12 +10:00
const {
builders: {
breakParent,
2018-05-27 21:36:12 +10:00
concat,
join,
line,
literalline,
markAsRoot,
2018-05-27 21:36:12 +10:00
hardline,
softline,
ifBreak,
2018-05-27 21:36:12 +10:00
fill,
align,
indent,
group,
},
2021-01-04 10:04:30 +08:00
utils: { normalizeDoc, getDocParts },
2018-05-27 21:36:12 +10:00
printer: { printDocToString },
2020-01-14 18:58:42 +08:00
} = require("../document");
const { replaceEndOfLineWith } = require("../common/util");
2020-06-26 20:30:21 +08:00
const embed = require("./embed");
const { insertPragma } = require("./pragma");
const { locStart, locEnd } = require("./loc");
const preprocess = require("./print-preprocess");
const clean = require("./clean");
const {
getFencedCodeBlockValue,
hasGitDiffFriendlyOrderedList,
splitText,
punctuationPattern,
INLINE_NODE_TYPES,
INLINE_NODE_WRAPPER_TYPES,
2020-06-23 03:52:50 -07:00
isAutolink,
} = require("./utils");
/**
* @typedef {import("../document").Doc} Doc
*/
2020-04-09 13:22:28 +08:00
const TRAILING_HARDLINE_NODES = new Set(["importExport"]);
const SINGLE_LINE_NODE_TYPES = ["heading", "tableCell", "link", "wikiLink"];
2020-04-09 13:22:28 +08:00
const SIBLING_NODE_TYPES = new Set([
"listItem",
"definition",
"footnoteDefinition",
]);
2017-10-11 17:46:44 -05:00
function genericPrint(path, options, print) {
const node = path.getValue();
if (shouldRemainTheSameContent(path)) {
return concat(
splitText(
options.originalText.slice(
node.position.start.offset,
node.position.end.offset
),
options
2018-11-07 09:12:25 +08:00
).map((node) =>
node.type === "word"
? node.value
: node.value === ""
? ""
: printLine(path, node.value, options)
)
2017-10-11 17:46:44 -05:00
);
}
switch (node.type) {
case "front-matter":
return options.originalText.slice(
node.position.start.offset,
node.position.end.offset
);
2017-10-11 17:46:44 -05:00
case "root":
2018-04-27 14:01:14 +07:00
if (node.children.length === 0) {
return "";
}
2018-08-13 22:23:09 +08:00
return concat([
normalizeDoc(printRoot(path, options, print)),
2020-04-09 13:22:28 +08:00
!TRAILING_HARDLINE_NODES.has(getLastDescendantNode(node).type)
2018-08-13 22:23:09 +08:00
? hardline
: "",
]);
2017-10-11 17:46:44 -05:00
case "paragraph":
return printChildren(path, options, print, {
postprocessor: fill,
});
case "sentence":
return printChildren(path, options, print);
2020-06-23 03:52:50 -07:00
case "word": {
let escapedValue = node.value
2020-11-16 01:03:35 +08:00
.replace(/\*/g, "\\$&") // escape all `*`
.replace(
new RegExp(
[
`(^|${punctuationPattern})(_+)`,
`(_+)(${punctuationPattern}|$)`,
].join("|"),
"g"
),
(_, text1, underscore1, underscore2, text2) =>
(underscore1
? `${text1}${underscore1}`
: `${underscore2}${text2}`
).replace(/_/g, "\\_")
); // escape all `_` except concating with non-punctuation, e.g. `1_2_3` is not considered emphasis
2020-06-23 03:52:50 -07:00
const isFirstSentence = (node, name, index) =>
node.type === "sentence" && index === 0;
const isLastChildAutolink = (node, name, index) =>
isAutolink(node.children[index - 1]);
2020-06-23 03:52:50 -07:00
if (
escapedValue !== node.value &&
(path.match(undefined, isFirstSentence, isLastChildAutolink) ||
path.match(
undefined,
isFirstSentence,
(node, name, index) => node.type === "emphasis" && index === 0,
isLastChildAutolink
))
) {
// backslash is parsed as part of autolinks, so we need to remove it
escapedValue = escapedValue.replace(/^(\\?[*_])+/, (prefix) =>
prefix.replace(/\\/g, "")
);
}
return escapedValue;
}
case "whitespace": {
const parentNode = path.getParentNode();
const index = parentNode.children.indexOf(node);
const nextNode = parentNode.children[index + 1];
const proseWrap =
// leading char that may cause different syntax
2020-04-10 21:20:40 +08:00
nextNode && /^>|^([*+-]|#{1,6}|\d+[).])$/.test(nextNode.value)
? "never"
: options.proseWrap;
return printLine(path, node.value, { proseWrap });
}
2017-10-11 17:46:44 -05:00
case "emphasis": {
2020-06-23 03:52:50 -07:00
let style;
if (isAutolink(node.children[0])) {
2020-06-23 03:52:50 -07:00
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 hasPrevOrNextWord = // `1*2*3` is considered emphasis but `1_2_3` is not
(prevNode &&
prevNode.type === "sentence" &&
prevNode.children.length > 0 &&
2020-07-05 00:34:35 +08:00
getLast(prevNode.children).type === "word" &&
!getLast(prevNode.children).hasTrailingPunctuation) ||
2020-06-23 03:52:50 -07:00
(nextNode &&
nextNode.type === "sentence" &&
nextNode.children.length > 0 &&
nextNode.children[0].type === "word" &&
!nextNode.children[0].hasLeadingPunctuation);
style =
hasPrevOrNextWord || getAncestorNode(path, "emphasis") ? "*" : "_";
}
2017-10-11 17:46:44 -05:00
return concat([style, printChildren(path, options, print), style]);
}
case "strong":
return concat(["**", printChildren(path, options, print), "**"]);
case "delete":
return concat(["~~", printChildren(path, options, print), "~~"]);
case "inlineCode": {
2020-07-05 00:34:35 +08:00
const backtickCount = getMinNotPresentContinuousCount(node.value, "`");
const style = "`".repeat(backtickCount || 1);
2020-06-23 03:52:50 -07:00
const gap = backtickCount && !/^\s/.test(node.value) ? " " : "";
return concat([style, gap, node.value, gap, style]);
2017-10-11 17:46:44 -05:00
}
case "wikiLink": {
let contents = "";
if (options.proseWrap === "preserve") {
contents = node.value;
} else {
contents = node.value.replace(/[\t\n]+/g, " ");
}
return concat(["[[", contents, "]]"]);
}
2017-10-11 17:46:44 -05:00
case "link":
switch (options.originalText[node.position.start.offset]) {
case "<": {
const mailto = "mailto:";
const url =
// <hello@example.com> is parsed as { url: "mailto:hello@example.com" }
node.url.startsWith(mailto) &&
options.originalText.slice(
node.position.start.offset + 1,
node.position.start.offset + 1 + mailto.length
) !== mailto
? node.url.slice(mailto.length)
: node.url;
return concat(["<", url, ">"]);
}
case "[":
return concat([
2017-10-11 17:46:44 -05:00
"[",
printChildren(path, options, print),
"](",
printUrl(node.url, ")"),
printTitle(node.title, options),
2017-10-11 17:46:44 -05:00
")",
]);
default:
return options.originalText.slice(
node.position.start.offset,
node.position.end.offset
);
}
2017-10-11 17:46:44 -05:00
case "image":
return concat([
"![",
node.alt || "",
"](",
printUrl(node.url, ")"),
printTitle(node.title, options),
2017-10-11 17:46:44 -05:00
")",
]);
case "blockquote":
return concat(["> ", align("> ", printChildren(path, options, print))]);
case "heading":
return concat([
"#".repeat(node.depth) + " ",
printChildren(path, options, print),
]);
case "code": {
if (node.isIndented) {
2017-10-11 17:46:44 -05:00
// indented code block
2017-11-11 15:05:07 +08:00
const alignment = " ".repeat(4);
2017-10-11 17:46:44 -05:00
return align(
2017-11-11 15:05:07 +08:00
alignment,
2018-12-08 18:28:29 +08:00
concat([
alignment,
concat(replaceEndOfLineWith(node.value, hardline)),
])
2017-10-11 17:46:44 -05:00
);
}
// fenced code block
const styleUnit = options.__inJsTemplate ? "~" : "`";
const style = styleUnit.repeat(
2020-07-05 00:34:35 +08:00
Math.max(3, getMaxContinuousCount(node.value, styleUnit) + 1)
2017-10-11 17:46:44 -05:00
);
return concat([
style,
node.lang || "",
2020-06-23 03:52:50 -07:00
node.meta ? " " + node.meta : "",
2017-10-11 17:46:44 -05:00
hardline,
2018-12-08 18:28:29 +08:00
concat(
replaceEndOfLineWith(
getFencedCodeBlockValue(node, options.originalText),
hardline
)
),
2017-10-11 17:46:44 -05:00
hardline,
style,
]);
}
case "html": {
const parentNode = path.getParentNode();
const value =
2020-07-05 00:34:35 +08:00
parentNode.type === "root" && getLast(parentNode.children) === node
? node.value.trimEnd()
: node.value;
2020-04-10 21:20:40 +08:00
const isHtmlComment = /^<!--[\S\s]*-->$/.test(value);
2018-12-08 18:28:29 +08:00
return concat(
replaceEndOfLineWith(
value,
isHtmlComment ? hardline : markAsRoot(literalline)
)
);
2017-10-11 17:46:44 -05:00
}
case "list": {
const nthSiblingIndex = getNthListSiblingIndex(
node,
path.getParentNode()
2017-10-11 17:46:44 -05:00
);
const isGitDiffFriendlyOrderedList = hasGitDiffFriendlyOrderedList(
node,
options
);
2017-10-11 17:46:44 -05:00
return printChildren(path, options, print, {
processor: (childPath, index) => {
const prefix = getPrefix();
const childNode = childPath.getValue();
if (
childNode.children.length === 2 &&
childNode.children[1].type === "html" &&
childNode.children[0].position.start.column !==
childNode.children[1].position.start.column
) {
return concat([
prefix,
printListItem(childPath, options, print, prefix),
]);
}
2017-11-11 15:05:07 +08:00
return concat([
prefix,
align(
" ".repeat(prefix.length),
printListItem(childPath, options, print, prefix)
),
2017-11-11 15:05:07 +08:00
]);
function getPrefix() {
const rawPrefix = node.ordered
? (index === 0
? node.start
2018-04-11 22:39:13 -06:00
: isGitDiffFriendlyOrderedList
2018-11-07 09:12:25 +08:00
? 1
: node.start + index) +
(nthSiblingIndex % 2 === 0 ? ". " : ") ")
2018-04-11 22:39:13 -06:00
: nthSiblingIndex % 2 === 0
2018-11-07 09:12:25 +08:00
? "- "
: "* ";
return node.isAligned ||
/* workaround for https://github.com/remarkjs/remark/issues/315 */ node.hasIndentedCodeblock
? alignListPrefix(rawPrefix, options)
: rawPrefix;
}
2017-10-11 17:46:44 -05:00
},
});
}
case "thematicBreak": {
const counter = getAncestorCounter(path, "list");
if (counter === -1) {
return "---";
}
const nthSiblingIndex = getNthListSiblingIndex(
path.getParentNode(counter),
path.getParentNode(counter + 1)
);
return nthSiblingIndex % 2 === 0 ? "***" : "---";
}
2017-10-11 17:46:44 -05:00
case "linkReference":
return concat([
"[",
printChildren(path, options, print),
"]",
node.referenceType === "full"
? concat(["[", node.identifier, "]"])
2018-04-11 22:39:13 -06:00
: node.referenceType === "collapsed"
2018-11-07 09:12:25 +08:00
? "[]"
: "",
2017-10-11 17:46:44 -05:00
]);
case "imageReference":
switch (node.referenceType) {
case "full":
return concat(["![", node.alt || "", "][", node.identifier, "]"]);
2017-10-11 17:46:44 -05:00
default:
return concat([
"![",
node.alt,
"]",
node.referenceType === "collapsed" ? "[]" : "",
]);
}
case "definition": {
const lineOrSpace = options.proseWrap === "always" ? line : " ";
return group(
concat([
concat(["[", node.identifier, "]:"]),
indent(
concat([
lineOrSpace,
printUrl(node.url),
node.title === null
? ""
: concat([lineOrSpace, printTitle(node.title, options, false)]),
])
),
])
);
}
2020-08-21 09:43:24 +08:00
// `footnote` requires `.use(footnotes, {inlineNotes: true})`, we are not using this option
// https://github.com/remarkjs/remark-footnotes#optionsinlinenotes
/* istanbul ignore next */
2017-10-11 17:46:44 -05:00
case "footnote":
return concat(["[^", printChildren(path, options, print), "]"]);
case "footnoteReference":
return concat(["[^", node.identifier, "]"]);
case "footnoteDefinition": {
const nextNode = path.getParentNode().children[path.getName() + 1];
const shouldInlineFootnote =
node.children.length === 1 &&
node.children[0].type === "paragraph" &&
(options.proseWrap === "never" ||
(options.proseWrap === "preserve" &&
node.children[0].position.start.line ===
node.children[0].position.end.line));
2017-10-11 17:46:44 -05:00
return concat([
"[^",
node.identifier,
"]: ",
shouldInlineFootnote
? printChildren(path, options, print)
: group(
concat([
align(
2020-06-23 03:52:50 -07:00
" ".repeat(4),
printChildren(path, options, print, {
2020-12-17 14:31:51 +08:00
processor: (childPath, index) =>
index === 0
? group(concat([softline, childPath.call(print)]))
2020-12-17 14:31:51 +08:00
: childPath.call(print),
})
),
nextNode && nextNode.type === "footnoteDefinition"
? softline
: "",
])
),
2017-10-11 17:46:44 -05:00
]);
}
2017-10-11 17:46:44 -05:00
case "table":
return printTable(path, options, print);
case "tableCell":
return printChildren(path, options, print);
case "break":
return /\s/.test(options.originalText[node.position.start.offset])
? concat([" ", markAsRoot(literalline)])
: concat(["\\", hardline]);
2018-05-15 14:22:41 -03:00
case "liquidNode":
2018-12-08 18:28:29 +08:00
return concat(replaceEndOfLineWith(node.value, hardline));
2018-08-13 22:23:09 +08:00
// MDX
2020-09-27 08:46:19 +08:00
// fallback to the original text if multiparser failed
// or `embeddedLanguageFormatting: "off"`
2018-08-13 22:23:09 +08:00
case "importExport":
return concat([node.value, hardline]);
2020-09-27 08:46:19 +08:00
case "jsx":
return node.value;
2018-09-05 21:07:37 +08:00
case "math":
return concat([
"$$",
hardline,
node.value
2018-12-08 18:28:29 +08:00
? concat([
concat(replaceEndOfLineWith(node.value, hardline)),
hardline,
])
2018-09-05 21:07:37 +08:00
: "",
"$$",
]);
case "inlineMath": {
// remark-math trims content but we don't want to remove whitespaces
// since it's very possible that it's recognized as math accidentally
return options.originalText.slice(locStart(node), locEnd(node));
}
2018-08-13 22:23:09 +08:00
2017-10-11 17:46:44 -05:00
case "tableRow": // handled in "table"
case "listItem": // handled in "list"
2017-10-11 17:46:44 -05:00
default:
2020-08-21 09:43:24 +08:00
/* istanbul ignore next */
2017-10-11 17:46:44 -05:00
throw new Error(`Unknown markdown type ${JSON.stringify(node.type)}`);
}
}
function printListItem(path, options, print, listPrefix) {
const node = path.getValue();
const prefix = node.checked === null ? "" : node.checked ? "[x] " : "[ ] ";
return concat([
prefix,
printChildren(path, options, print, {
processor: (childPath, index) => {
if (index === 0 && childPath.getValue().type !== "list") {
return align(" ".repeat(prefix.length), childPath.call(print));
}
const alignment = " ".repeat(
clamp(options.tabWidth - listPrefix.length, 0, 3) // 4+ will cause indented code block
);
return concat([alignment, align(alignment, childPath.call(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
);
}
2017-10-11 17:46:44 -05:00
function getNthSiblingIndex(node, parentNode, condition) {
condition = condition || (() => true);
2017-10-11 17:46:44 -05:00
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 getAncestorCounter(path, typeOrTypes) {
2017-10-11 17:46:44 -05:00
const types = [].concat(typeOrTypes);
let counter = -1;
2017-10-11 17:46:44 -05:00
let ancestorNode;
while ((ancestorNode = path.getParentNode(++counter))) {
2019-11-17 18:06:14 +08:00
if (types.includes(ancestorNode.type)) {
return counter;
2017-10-11 17:46:44 -05:00
}
}
return -1;
}
function getAncestorNode(path, typeOrTypes) {
const counter = getAncestorCounter(path, typeOrTypes);
return counter === -1 ? null : path.getParentNode(counter);
2017-10-11 17:46:44 -05:00
}
function printLine(path, value, options) {
if (options.proseWrap === "preserve" && value === "\n") {
return hardline;
}
const isBreakable =
options.proseWrap === "always" &&
!getAncestorNode(path, SINGLE_LINE_NODE_TYPES);
return value !== ""
2018-04-11 22:39:13 -06:00
? isBreakable
? line
: " "
: isBreakable
2018-11-07 09:12:25 +08:00
? softline
: "";
}
2017-10-11 17:46:44 -05:00
function printTable(path, options, print) {
2021-01-04 10:04:30 +08:00
const hardlineWithoutBreakParent = getDocParts(hardline)[0];
2017-10-11 17:46:44 -05:00
const node = path.getValue();
const columnMaxWidths = [];
// { [rowIndex: number]: { [columnIndex: number]: {text: string, width: number} } }
const contents = path.map(
(rowPath) =>
rowPath.map((cellPath, columnIndex) => {
const text = printDocToString(cellPath.call(print), options).formatted;
const width = getStringWidth(text);
columnMaxWidths[columnIndex] = Math.max(
columnMaxWidths[columnIndex] || 3, // minimum width = 3 (---, :--, :-:, --:)
width
);
return { text, width };
}, "children"),
"children"
);
2017-10-11 17:46:44 -05:00
const alignedTable = printTableContents(/* isCompact */ false);
if (options.proseWrap !== "never") {
return concat([breakParent, alignedTable]);
}
// Only if the --prose-wrap never is set and it exceeds the print width.
const compactTable = printTableContents(/* isCompact */ true);
return concat([breakParent, group(ifBreak(compactTable, alignedTable))]);
function printTableContents(isCompact) {
/** @type{Doc[]} */
const parts = [printRow(contents[0], isCompact), printAlign(isCompact)];
if (contents.length > 1) {
parts.push(
join(
hardlineWithoutBreakParent,
contents
.slice(1)
.map((rowContents) => printRow(rowContents, isCompact))
)
);
}
return join(hardlineWithoutBreakParent, parts);
}
function printAlign(isCompact) {
const align = columnMaxWidths.map((width, index) => {
const align = node.align[index];
const first = align === "center" || align === "left" ? ":" : "-";
const last = align === "center" || align === "right" ? ":" : "-";
const middle = isCompact ? "-" : "-".repeat(width - 2);
return `${first}${middle}${last}`;
});
return `| ${align.join(" | ")} |`;
2017-10-11 17:46:44 -05:00
}
function printRow(rowContents, isCompact) {
const columns = rowContents.map(({ text, width }, columnIndex) => {
if (isCompact) {
return text;
}
const spaces = columnMaxWidths[columnIndex] - width;
const align = node.align[columnIndex];
let before = 0;
if (align === "right") {
before = spaces;
} else if (align === "center") {
before = Math.floor(spaces / 2);
}
const after = spaces - before;
return `${" ".repeat(before)}${text}${" ".repeat(after)}`;
});
2017-10-11 17:46:44 -05:00
return `| ${columns.join(" | ")} |`;
2017-10-11 17:46:44 -05:00
}
}
function printRoot(path, options, print) {
/** @typedef {{ index: number, offset: number }} IgnorePosition */
/** @type {Array<{start: IgnorePosition, end: IgnorePosition}>} */
const ignoreRanges = [];
/** @type {IgnorePosition | null} */
let ignoreStart = null;
2020-01-14 23:30:18 +08:00
const { children } = path.getValue();
children.forEach((childNode, index) => {
switch (isPrettierIgnore(childNode)) {
case "start":
if (ignoreStart === null) {
ignoreStart = { index, offset: childNode.position.end.offset };
}
break;
case "end":
if (ignoreStart !== null) {
ignoreRanges.push({
start: ignoreStart,
end: { index, offset: childNode.position.start.offset },
});
ignoreStart = null;
}
break;
default:
// do nothing
break;
}
});
return printChildren(path, options, print, {
processor: (childPath, index) => {
if (ignoreRanges.length !== 0) {
const ignoreRange = ignoreRanges[0];
if (index === ignoreRange.start.index) {
return concat([
children[ignoreRange.start.index].value,
options.originalText.slice(
ignoreRange.start.offset,
ignoreRange.end.offset
),
children[ignoreRange.end.index].value,
]);
}
if (ignoreRange.start.index < index && index < ignoreRange.end.index) {
return false;
}
if (index === ignoreRange.end.index) {
ignoreRanges.shift();
return false;
}
}
return childPath.call(print);
},
});
}
2017-10-11 17:46:44 -05:00
function printChildren(path, options, print, events) {
events = events || {};
const postprocessor = events.postprocessor || concat;
const processor = events.processor || ((childPath) => childPath.call(print));
const node = path.getValue();
const parts = [];
let lastChildNode;
path.each((childPath, index) => {
2017-10-11 17:46:44 -05:00
const childNode = childPath.getValue();
const result = processor(childPath, index);
2017-10-11 17:46:44 -05:00
if (result !== false) {
const data = {
parts,
prevNode: lastChildNode,
parentNode: node,
options,
};
if (!shouldNotPrePrintHardline(childNode, data)) {
parts.push(hardline);
2020-08-21 09:43:24 +08:00
// Can't find a case to pass `shouldPrePrintTripleHardline`
/* istanbul ignore next */
2020-04-09 13:22:28 +08:00
if (lastChildNode && TRAILING_HARDLINE_NODES.has(lastChildNode.type)) {
2018-08-13 22:23:09 +08:00
if (shouldPrePrintTripleHardline(childNode, data)) {
parts.push(hardline);
}
} else {
if (
shouldPrePrintDoubleHardline(childNode, data) ||
shouldPrePrintTripleHardline(childNode, data)
) {
parts.push(hardline);
}
2017-10-11 17:46:44 -05:00
2018-08-13 22:23:09 +08:00
if (shouldPrePrintTripleHardline(childNode, data)) {
parts.push(hardline);
}
2017-10-11 17:46:44 -05:00
}
}
parts.push(result);
lastChildNode = childNode;
}
}, "children");
return postprocessor(parts);
}
2018-08-13 22:23:09 +08:00
function getLastDescendantNode(node) {
let current = node;
2020-12-17 14:46:59 +08:00
while (isNonEmptyArray(current.children)) {
2018-08-13 22:23:09 +08:00
current = current.children[current.children.length - 1];
}
return current;
}
/** @return {false | 'next' | 'start' | 'end'} */
2017-10-11 17:46:44 -05:00
function isPrettierIgnore(node) {
if (node.type !== "html") {
return false;
}
const match = node.value.match(
/^<!--\s*prettier-ignore(?:-(start|end))?\s*-->$/
2017-10-11 17:46:44 -05:00
);
return match === null ? false : match[1] ? match[1] : "next";
2017-10-11 17:46:44 -05:00
}
2019-11-15 11:01:58 +01:00
function shouldNotPrePrintHardline(node, data) {
const isFirstNode = data.parts.length === 0;
2019-11-17 18:06:14 +08:00
const isInlineNode = INLINE_NODE_TYPES.includes(node.type);
2017-10-11 17:46:44 -05:00
const isInlineHTML =
node.type === "html" &&
2019-11-17 18:06:14 +08:00
INLINE_NODE_WRAPPER_TYPES.includes(data.parentNode.type);
2017-10-11 17:46:44 -05:00
2019-11-15 11:01:58 +01:00
return isFirstNode || isInlineNode || isInlineHTML;
2017-10-11 17:46:44 -05:00
}
2019-11-15 11:01:58 +01:00
function shouldPrePrintDoubleHardline(node, data) {
const isSequence = (data.prevNode && data.prevNode.type) === node.type;
2020-04-09 13:22:28 +08:00
const isSiblingNode = isSequence && SIBLING_NODE_TYPES.has(node.type);
2017-10-11 17:46:44 -05:00
2019-11-15 11:01:58 +01:00
const isInTightListItem =
data.parentNode.type === "listItem" && !data.parentNode.loose;
const isPrevNodeLooseListItem =
data.prevNode && data.prevNode.type === "listItem" && data.prevNode.loose;
const isPrevNodePrettierIgnore = isPrettierIgnore(data.prevNode) === "next";
2017-10-11 17:46:44 -05:00
const isBlockHtmlWithoutBlankLineBetweenPrevHtml =
2019-11-15 11:01:58 +01:00
node.type === "html" &&
data.prevNode &&
data.prevNode.type === "html" &&
data.prevNode.position.end.line + 1 === node.position.start.line;
const isHtmlDirectAfterListItem =
node.type === "html" &&
data.parentNode.type === "listItem" &&
data.prevNode &&
data.prevNode.type === "paragraph" &&
data.prevNode.position.end.line + 1 === node.position.start.line;
2017-10-11 17:46:44 -05:00
return (
isPrevNodeLooseListItem ||
!(
isSiblingNode ||
isInTightListItem ||
isPrevNodePrettierIgnore ||
isBlockHtmlWithoutBlankLineBetweenPrevHtml ||
isHtmlDirectAfterListItem
)
2017-10-11 17:46:44 -05:00
);
}
function shouldPrePrintTripleHardline(node, data) {
const isPrevNodeList = data.prevNode && data.prevNode.type === "list";
const isIndentedCode = node.type === "code" && node.isIndented;
2017-10-11 17:46:44 -05:00
return isPrevNodeList && isIndentedCode;
}
function shouldRemainTheSameContent(path) {
const ancestorNode = getAncestorNode(path, [
"linkReference",
"imageReference",
]);
return (
ancestorNode &&
(ancestorNode.type !== "linkReference" ||
ancestorNode.referenceType !== "full")
);
}
function printUrl(url, dangerousCharOrChars) {
const dangerousChars = [" "].concat(dangerousCharOrChars || []);
return new RegExp(dangerousChars.map((x) => `\\${x}`).join("|")).test(url)
? `<${url}>`
: url;
}
function printTitle(title, options, printSpace) {
if (printSpace == null) {
printSpace = true;
}
if (!title) {
return "";
}
if (printSpace) {
return " " + printTitle(title, options, false);
}
2020-06-23 03:52:50 -07:00
// title is escaped after `remark-parse` v7
title = title.replace(/\\(["')])/g, "$1");
if (title.includes('"') && title.includes("'") && !title.includes(")")) {
return `(${title})`; // avoid escaped quotes
}
// faster than using RegExps: https://jsperf.com/performance-of-match-vs-split
const singleCount = title.split("'").length - 1;
const doubleCount = title.split('"').length - 1;
const quote =
singleCount > doubleCount
? '"'
2018-04-11 22:39:13 -06:00
: doubleCount > singleCount
2018-11-07 09:12:25 +08:00
? "'"
: options.singleQuote
? "'"
: '"';
2020-06-23 03:52:50 -07:00
title = title.replace(/\\/, "\\\\");
title = title.replace(new RegExp(`(${quote})`, "g"), "\\$1");
return `${quote}${title}${quote}`;
2017-10-11 17:46:44 -05:00
}
function clamp(value, min, max) {
return value < min ? min : value > max ? max : value;
}
function hasPrettierIgnore(path) {
const index = +path.getName();
if (index === 0) {
return false;
}
const prevNode = path.getParentNode().children[index - 1];
return isPrettierIgnore(prevNode) === "next";
}
2017-12-26 12:23:50 +11:00
module.exports = {
2018-09-03 23:27:50 +08:00
preprocess,
2017-12-26 12:23:50 +11:00
print: genericPrint,
embed,
massageAstNode: clean,
hasPrettierIgnore,
insertPragma,
2017-12-26 12:23:50 +11:00
};