prettier/src/doc-utils.js

160 lines
3.2 KiB
JavaScript

"use strict";
function traverseDoc(doc, onEnter, onExit) {
var hasStopped = false;
function traverseDocRec(doc) {
if (onEnter) {
hasStopped = hasStopped || onEnter(doc) === false;
}
if (hasStopped) {
return;
}
if (doc.type === "concat") {
for (var i = 0; i < doc.parts.length; i++) {
traverseDocRec(doc.parts[i]);
}
} else if (doc.type === "if-break") {
if (doc.breakContents) {
traverseDocRec(doc.breakContents);
}
if (doc.flatContents) {
traverseDocRec(doc.flatContents);
}
} else if (doc.contents) {
traverseDocRec(doc.contents);
}
if (onExit) {
onExit(doc);
}
}
traverseDocRec(doc);
}
function mapDoc(doc, func) {
doc = func(doc);
if (doc.type === "concat") {
return Object.assign({}, doc, {
parts: doc.parts.map(d => mapDoc(d, func))
});
} else if (doc.type === "if-break") {
return Object.assign({}, doc, {
breakContents: doc.breakContents && mapDoc(doc.breakContents, func),
flatContents: doc.flatContents && mapDoc(doc.flatContents, func)
});
} else if (doc.contents) {
return Object.assign({}, doc, { contents: mapDoc(doc.contents, func) });
} else {
return doc;
}
}
function findInDoc(doc, fn, defaultValue) {
var result = defaultValue;
traverseDoc(doc, function(doc) {
var maybeResult = fn(doc);
if (maybeResult !== undefined) {
result = maybeResult;
return false;
}
});
return result;
}
function isEmpty(n) {
return typeof n === "string" && n.length === 0;
}
function getFirstString(doc) {
return findInDoc(
doc,
doc => {
if (typeof doc === "string" && doc.trim().length !== 0) {
return doc;
}
},
null
);
}
function isLineNext(doc) {
return findInDoc(
doc,
doc => {
if (typeof doc === "string") {
return false;
}
if (doc.type === "line") {
return true;
}
},
false
);
}
function willBreak(doc) {
return findInDoc(
doc,
doc => {
if (doc.type === "group" && doc.break) {
return true;
}
if (doc.type === "line" && doc.hard) {
return true;
}
if (doc.type === "break-parent") {
return true;
}
},
false
);
}
function breakParentGroup(groupStack) {
if (groupStack.length > 0) {
const parentGroup = groupStack[groupStack.length - 1];
// Breaks are not propagated through conditional groups because
// the user is expected to manually handle what breaks.
if (!parentGroup.expandedStates) {
parentGroup.break = true;
}
}
return null;
}
function propagateBreaks(doc) {
const groupStack = [];
traverseDoc(
doc,
doc => {
if (doc.type === "break-parent") {
breakParentGroup(groupStack);
}
if (doc.type === "group") {
groupStack.push(doc);
}
},
doc => {
if (doc.type === "group") {
const group = groupStack.pop();
if (group.break) {
breakParentGroup(groupStack);
}
}
}
);
}
module.exports = {
isEmpty,
getFirstString,
willBreak,
isLineNext,
traverseDoc,
mapDoc,
propagateBreaks
};