Split out location extraction into plugin config (#3777)

* Split out location extraction into plugin config

* Reduce set of exported function, rebase to current master

* Pass custom locStart/locEnd fn's directly to affected methods

* (wip) always use locStart/locEnd from plugin

* Convert graphql implementation

* Convert HTML implementation

* (wip) convert JS implementation

* Remove unused variables

* Continue conversion of JS implementation

* Replace "util.locStart" with direct call to "options.locStart"

* Remove util closure

* Add unit test for structure of shared util

* Fix linting error, simplify locStart/End implementations
master
Christian Zosel 2018-02-09 13:17:48 +01:00 committed by Lucas Azzola
parent 7b2349dcd2
commit fac08034e7
20 changed files with 544 additions and 277 deletions

View File

@ -4,7 +4,7 @@ const docblock = require("jest-docblock");
const version = require("./package.json").version;
const util = require("./src/common/util");
const privateUtil = require("./src/common/util");
const getSupportInfo = require("./src/common/support").getSupportInfo;
const comments = require("./src/main/comments");
@ -111,7 +111,7 @@ function formatWithCursor(text, opts, addAlignmentSize) {
const cursorNodeAndParents = findNodeAtOffset(ast, opts.cursorOffset, opts);
const cursorNode = cursorNodeAndParents.node;
if (cursorNode) {
cursorOffset = opts.cursorOffset - util.locStart(cursorNode);
cursorOffset = opts.cursorOffset - opts.locStart(cursorNode);
opts.cursorNode = cursorNode;
}
}
@ -145,7 +145,7 @@ function format(text, opts, addAlignmentSize) {
return formatWithCursor(text, opts, addAlignmentSize).formatted;
}
function findSiblingAncestors(startNodeAndParents, endNodeAndParents) {
function findSiblingAncestors(startNodeAndParents, endNodeAndParents, opts) {
let resultStartNode = startNodeAndParents.node;
let resultEndNode = endNodeAndParents.node;
@ -160,7 +160,7 @@ function findSiblingAncestors(startNodeAndParents, endNodeAndParents) {
if (
endParent.type !== "Program" &&
endParent.type !== "File" &&
util.locStart(endParent) >= util.locStart(startNodeAndParents.node)
opts.locStart(endParent) >= opts.locStart(startNodeAndParents.node)
) {
resultEndNode = endParent;
} else {
@ -172,7 +172,7 @@ function findSiblingAncestors(startNodeAndParents, endNodeAndParents) {
if (
startParent.type !== "Program" &&
startParent.type !== "File" &&
util.locEnd(startParent) <= util.locEnd(endNodeAndParents.node)
opts.locEnd(startParent) <= opts.locEnd(endNodeAndParents.node)
) {
resultStartNode = startParent;
} else {
@ -189,8 +189,8 @@ function findSiblingAncestors(startNodeAndParents, endNodeAndParents) {
function findNodeAtOffset(node, offset, options, predicate, parentNodes) {
predicate = predicate || (() => true);
parentNodes = parentNodes || [];
const start = util.locStart(node);
const end = util.locEnd(node);
const start = options.locStart(node, options.locStart);
const end = options.locEnd(node, options.locEnd);
if (start <= offset && offset <= end) {
for (const childNode of comments.getSortedChildNodes(
node,
@ -335,12 +335,19 @@ function calculateRange(text, opts, ast) {
const siblingAncestors = findSiblingAncestors(
startNodeAndParents,
endNodeAndParents
endNodeAndParents,
opts
);
const startNode = siblingAncestors.startNode;
const endNode = siblingAncestors.endNode;
const rangeStart = Math.min(util.locStart(startNode), util.locStart(endNode));
const rangeEnd = Math.max(util.locEnd(startNode), util.locEnd(endNode));
const rangeStart = Math.min(
opts.locStart(startNode, opts.locStart),
opts.locStart(endNode, opts.locStart)
);
const rangeEnd = Math.max(
opts.locEnd(startNode, opts.locEnd),
opts.locEnd(endNode, opts.locEnd)
);
return {
rangeStart: rangeStart,
@ -367,7 +374,10 @@ function formatRange(text, opts, ast) {
);
const indentString = text.slice(rangeStart2, rangeStart);
const alignmentSize = util.getAlignmentSize(indentString, opts.tabWidth);
const alignmentSize = privateUtil.getAlignmentSize(
indentString,
opts.tabWidth
);
const rangeFormatted = format(
rangeString,

View File

@ -154,7 +154,13 @@ FastPath.prototype.needsParens = function(options) {
// Closure compiler requires that type casted expressions to be surrounded by
// parentheses.
if (util.hasClosureCompilerTypeCastComment(options.originalText, node)) {
if (
util.hasClosureCompilerTypeCastComment(
options.originalText,
node,
options.locEnd
)
) {
return true;
}
@ -486,7 +492,7 @@ FastPath.prototype.needsParens = function(options) {
// See corresponding workaround in printer.js case: "Literal"
((options.parser !== "typescript" && !parent.directive) ||
(options.parser === "typescript" &&
options.originalText.substr(util.locStart(node) - 1, 1) === "("))
options.originalText.substr(options.locStart(node) - 1, 1) === "("))
) {
// To avoid becoming a directive
const grandParent = this.getParentNode(1);

23
src/common/util-shared.js Normal file
View File

@ -0,0 +1,23 @@
"use strict";
const util = require("./util");
function isNextLineEmpty(text, node, options) {
return util.isNextLineEmpty(text, node, options.locEnd);
}
function getNextNonSpaceNonCommentCharacterIndex(text, node, options) {
return util.getNextNonSpaceNonCommentCharacterIndex(
text,
node,
options.locEnd
);
}
module.exports = {
isNextLineEmpty,
isNextLineEmptyAfterIndex: util.isNextLineEmptyAfterIndex,
getNextNonSpaceNonCommentCharacterIndex,
mapDoc: util.mapDoc,
makeString: util.makeString
};

View File

@ -187,7 +187,7 @@ function hasNewlineInRange(text, start, end) {
}
// Note: this function doesn't ignore leading comments unlike isNextLineEmpty
function isPreviousLineEmpty(text, node) {
function isPreviousLineEmpty(text, node, locStart) {
let idx = locStart(node) - 1;
idx = skipSpaces(text, idx, { backwards: true });
idx = skipNewline(text, idx, { backwards: true });
@ -211,11 +211,11 @@ function isNextLineEmptyAfterIndex(text, index) {
return hasNewline(text, idx);
}
function isNextLineEmpty(text, node) {
function isNextLineEmpty(text, node, locEnd) {
return isNextLineEmptyAfterIndex(text, locEnd(node));
}
function getNextNonSpaceNonCommentCharacterIndex(text, node) {
function getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd) {
let oldIdx = null;
let idx = locEnd(node);
while (idx !== oldIdx) {
@ -228,8 +228,10 @@ function getNextNonSpaceNonCommentCharacterIndex(text, node) {
return idx;
}
function getNextNonSpaceNonCommentCharacter(text, node) {
return text.charAt(getNextNonSpaceNonCommentCharacterIndex(text, node));
function getNextNonSpaceNonCommentCharacter(text, node, locEnd) {
return text.charAt(
getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd)
);
}
function hasSpaces(text, index, opts) {
@ -238,65 +240,6 @@ function hasSpaces(text, index, opts) {
return idx !== index;
}
function locStart(node) {
// Handle nodes with decorators. They should start at the first decorator
if (
node.declaration &&
node.declaration.decorators &&
node.declaration.decorators.length > 0
) {
return locStart(node.declaration.decorators[0]);
}
if (node.decorators && node.decorators.length > 0) {
return locStart(node.decorators[0]);
}
if (node.__location) {
return node.__location.startOffset;
}
if (node.range) {
return node.range[0];
}
if (typeof node.start === "number") {
return node.start;
}
if (node.source) {
return lineColumnToIndex(node.source.start, node.source.input.css) - 1;
}
if (node.loc) {
return node.loc.start;
}
}
function locEnd(node) {
const endNode = node.nodes && getLast(node.nodes);
if (endNode && node.source && !node.source.end) {
node = endNode;
}
let loc;
if (node.range) {
loc = node.range[1];
} else if (typeof node.end === "number") {
loc = node.end;
} else if (node.source) {
loc = lineColumnToIndex(node.source.end, node.source.input.css);
}
if (node.__location) {
return node.__location.endOffset;
}
if (node.typeAnnotation) {
return Math.max(loc, locEnd(node.typeAnnotation));
}
if (node.loc && !loc) {
return node.loc.end;
}
return loc;
}
// Super inefficient, needs to be cached.
function lineColumnToIndex(lineColumn, text) {
let index = 0;
@ -476,7 +419,7 @@ function isBlockComment(comment) {
return comment.type === "Block" || comment.type === "CommentBlock";
}
function hasClosureCompilerTypeCastComment(text, node) {
function hasClosureCompilerTypeCastComment(text, node, locEnd) {
// https://github.com/google/closure-compiler/wiki/Annotating-Types#type-casts
// Syntax example: var x = /** @type {string} */ (fruit);
return (
@ -486,7 +429,7 @@ function hasClosureCompilerTypeCastComment(text, node) {
comment.leading &&
isBlockComment(comment) &&
comment.value.match(/^\*\s*@type\s*{[^}]+}\s*$/) &&
getNextNonSpaceNonCommentCharacter(text, comment) === "("
getNextNonSpaceNonCommentCharacter(text, comment, locEnd) === "("
)
);
}
@ -839,8 +782,6 @@ module.exports = {
hasNewline,
hasNewlineInRange,
hasSpaces,
locStart,
locEnd,
setLocStart,
setLocEnd,
startsWithNoLookaheadToken,
@ -852,5 +793,7 @@ module.exports = {
printString,
printNumber,
hasIgnoreComment,
hasNodeIgnoreComment
hasNodeIgnoreComment,
lineColumnToIndex,
makeString
};

View File

@ -2,6 +2,10 @@
const printer = require("./printer-postcss");
const options = require("./options");
const privateUtil = require("../common/util");
const lineColumnToIndex = privateUtil.lineColumnToIndex;
const getLast = privateUtil.getLast;
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
@ -52,7 +56,23 @@ const postcss = {
get parse() {
return eval("require")("./parser-postcss");
},
astFormat: "postcss"
astFormat: "postcss",
locEnd: function(node) {
const endNode = node.nodes && getLast(node.nodes);
if (endNode && node.source && !node.source.end) {
node = endNode;
}
if (node.source) {
return lineColumnToIndex(node.source.end, node.source.input.css);
}
return null;
},
locStart: function(node) {
if (node.source) {
return lineColumnToIndex(node.source.start, node.source.input.css) - 1;
}
return null;
}
};
// TODO: switch these to just `postcss` and use `language` instead.

View File

@ -1,7 +1,8 @@
"use strict";
const clean = require("./clean");
const util = require("../common/util");
const privateUtil = require("../common/util");
const sharedUtil = require("../common/util-shared");
const doc = require("../doc");
const docBuilders = doc.builders;
const concat = docBuilders.concat;
@ -44,8 +45,8 @@ function genericPrint(path, options, print) {
return node.raws.content;
}
const text = options.originalText.slice(
util.locStart(node),
util.locEnd(node)
options.locStart(node),
options.locEnd(node)
);
const rawText = node.raws.text || node.text;
// Workaround a bug where the location is off.
@ -575,7 +576,7 @@ function genericPrint(path, options, print) {
return concat([node.value, " "]);
}
case "value-string": {
return util.printString(
return privateUtil.printString(
node.raws.quote + node.value + node.raws.quote,
options
);
@ -691,8 +692,8 @@ function printNodeSequence(path, options, print) {
const childNode = pathChild.getValue();
parts.push(
options.originalText.slice(
util.locStart(childNode),
util.locEnd(childNode)
options.locStart(childNode),
options.locEnd(childNode)
)
);
} else {
@ -702,9 +703,9 @@ function printNodeSequence(path, options, print) {
if (i !== node.nodes.length - 1) {
if (
(node.nodes[i + 1].type === "css-comment" &&
!util.hasNewline(
!privateUtil.hasNewline(
options.originalText,
util.locStart(node.nodes[i + 1]),
options.locStart(node.nodes[i + 1]),
{ backwards: true }
)) ||
(node.nodes[i + 1].type === "css-atrule" &&
@ -714,7 +715,13 @@ function printNodeSequence(path, options, print) {
parts.push(" ");
} else {
parts.push(hardline);
if (util.isNextLineEmpty(options.originalText, pathChild.getValue())) {
if (
sharedUtil.isNextLineEmpty(
options.originalText,
pathChild.getValue(),
options
)
) {
parts.push(hardline);
}
}
@ -739,7 +746,9 @@ const ADJUST_NUMBERS_REGEX = RegExp(
);
function adjustStrings(value, options) {
return value.replace(STRING_REGEX, match => util.printString(match, options));
return value.replace(STRING_REGEX, match =>
privateUtil.printString(match, options)
);
}
function quoteAttributeValue(value, options) {
@ -761,7 +770,7 @@ function adjustNumbers(value) {
function printNumber(rawNumber) {
return (
util
privateUtil
.printNumber(rawNumber)
// Remove trailing `.0`.
.replace(/\.0(?=$|e)/, "")
@ -789,6 +798,6 @@ function isWideKeywords(value) {
module.exports = {
print: genericPrint,
hasPrettierIgnore: util.hasIgnoreComment,
hasPrettierIgnore: privateUtil.hasIgnoreComment,
massageAstNode: clean
};

View File

@ -24,7 +24,19 @@ const parsers = {
get parse() {
return eval("require")("./parser-graphql");
},
astFormat: "graphql"
astFormat: "graphql",
locStart: function(node) {
if (typeof node.start === "number") {
return node.start;
}
return node.loc && node.loc.start;
},
locEnd: function(node) {
if (typeof node.end === "number") {
return node.end;
}
return node.loc && node.loc.end;
}
}
};

View File

@ -9,8 +9,8 @@ const softline = docBuilders.softline;
const group = docBuilders.group;
const indent = docBuilders.indent;
const ifBreak = docBuilders.ifBreak;
const util = require("../common/util");
const privateUtil = require("../common/util");
const sharedUtil = require("../common/util-shared");
function genericPrint(path, options, print) {
const n = path.getValue();
@ -30,7 +30,7 @@ function genericPrint(path, options, print) {
]);
}
case "OperationDefinition": {
const hasOperation = options.originalText[util.locStart(n)] !== "{";
const hasOperation = options.originalText[options.locStart(n)] !== "{";
const hasName = !!n.name;
return concat([
hasOperation ? n.operation : "",
@ -591,7 +591,11 @@ function printSequence(sequencePath, options, print) {
const printed = print(path);
if (
util.isNextLineEmpty(options.originalText, path.getValue()) &&
sharedUtil.isNextLineEmpty(
options.originalText,
path.getValue(),
options
) &&
i < count - 1
) {
return concat([printed, hardline]);
@ -618,7 +622,7 @@ function printComment(commentPath) {
module.exports = {
print: genericPrint,
hasPrettierIgnore: util.hasIgnoreComment,
hasPrettierIgnore: privateUtil.hasIgnoreComment,
printComment,
canAttachComment
};

View File

@ -22,7 +22,13 @@ const parsers = {
get parse() {
return eval("require")("./parser-glimmer");
},
astFormat: "glimmer"
astFormat: "glimmer",
locEnd: function(node) {
return node.loc && node.loc.end;
},
locStart: function(node) {
return node.loc && node.loc.start;
}
}
};

View File

@ -1,6 +1,6 @@
"use strict";
const util = require("../common/util");
const privateUtil = require("../common/util");
const doc = require("../doc");
const docUtils = doc.utils;
const docBuilders = doc.builders;
@ -66,7 +66,7 @@ function embed(path, print, textToDoc, options) {
return concat([
node.key,
'="',
util.hasNewlineInRange(node.value, 0, node.value.length)
privateUtil.hasNewlineInRange(node.value, 0, node.value.length)
? doc
: docUtils.removeLines(doc),
'"'
@ -87,7 +87,10 @@ function parseJavaScriptExpression(text, parsers) {
}
function getText(options, node) {
return options.originalText.slice(util.locStart(node), util.locEnd(node));
return options.originalText.slice(
options.locStart(node),
options.locEnd(node)
);
}
module.exports = embed;

View File

@ -27,7 +27,13 @@ const parsers = {
get parse() {
return eval("require")("./parser-parse5");
},
astFormat: "htmlparser2"
astFormat: "htmlparser2",
locEnd: function(node) {
return node.__location && node.__location.endOffset;
},
locStart: function(node) {
return node.__location && node.__location.startOffset;
}
}
};

View File

@ -1,7 +1,7 @@
"use strict";
const embed = require("./embed");
const util = require("../common/util");
const privateUtil = require("../common/util");
const docBuilders = require("../doc").builders;
const concat = docBuilders.concat;
const join = docBuilders.join;
@ -55,10 +55,10 @@ function genericPrint(path, options, print) {
const selfClose = voidTags[n.name] ? ">" : " />";
const children = printChildren(path, print);
const hasNewline = util.hasNewlineInRange(
const hasNewline = privateUtil.hasNewlineInRange(
options.originalText,
util.locStart(n),
util.locEnd(n)
options.locStart(n),
options.locEnd(n)
);
return group(
@ -117,5 +117,5 @@ function printChildren(path, print) {
module.exports = {
print: genericPrint,
embed,
hasPrettierIgnore: util.hasIgnoreComment
hasPrettierIgnore: privateUtil.hasIgnoreComment
};

View File

@ -2,10 +2,66 @@
const printer = require("./printer-estree");
const options = require("./options");
const privateUtil = require("../common/util");
// Based on:
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
const locStart = function(node) {
// Handle nodes with decorators. They should start at the first decorator
if (
node.declaration &&
node.declaration.decorators &&
node.declaration.decorators.length > 0
) {
return locStart(node.declaration.decorators[0]);
}
if (node.decorators && node.decorators.length > 0) {
return locStart(node.decorators[0]);
}
if (node.__location) {
return node.__location.startOffset;
}
if (node.range) {
return node.range[0];
}
if (typeof node.start === "number") {
return node.start;
}
if (node.loc) {
return node.loc.start;
}
return null;
};
const locEnd = function(node) {
const endNode = node.nodes && privateUtil.getLast(node.nodes);
if (endNode && node.source && !node.source.end) {
node = endNode;
}
let loc;
if (node.range) {
loc = node.range[1];
} else if (typeof node.end === "number") {
loc = node.end;
}
if (node.__location) {
return node.__location.endOffset;
}
if (node.typeAnnotation) {
return Math.max(loc, locEnd(node.typeAnnotation));
}
if (node.loc && !loc) {
return node.loc.end;
}
return loc;
};
const languages = [
{
name: "JavaScript",
@ -104,14 +160,18 @@ const typescript = {
get parse() {
return eval("require")("./parser-typescript");
},
astFormat: "estree"
astFormat: "estree",
locStart,
locEnd
};
const babylon = {
get parse() {
return eval("require")("./parser-babylon");
},
astFormat: "estree"
astFormat: "estree",
locStart,
locEnd
};
const parsers = {
@ -121,7 +181,9 @@ const parsers = {
get parse() {
return eval("require")("./parser-flow");
},
astFormat: "estree"
astFormat: "estree",
locStart,
locEnd
},
"typescript-eslint": typescript,
// TODO: Delete this in 2.0
@ -136,5 +198,7 @@ module.exports = {
languages,
options,
parsers,
printers
printers,
locStart,
locEnd
};

View File

@ -3,7 +3,8 @@
const assert = require("assert");
// TODO(azz): anything that imports from main shouldn't be in a `language-*` dir.
const comments = require("../main/comments");
const util = require("../common/util");
const privateUtil = require("../common/util");
const sharedUtil = require("../common/util-shared");
const isIdentifierName = require("esutils").keyword.isIdentifierNameES6;
const embed = require("./embed");
const clean = require("./clean");
@ -68,7 +69,7 @@ function genericPrint(path, options, printPath, args) {
node.decorators.length > 0 &&
// If the parent node is an export declaration, it will be
// responsible for printing node.decorators.
!util.getParentExportDeclaration(path)
!privateUtil.getParentExportDeclaration(path)
) {
let separator = hardline;
path.each(decoratorPath => {
@ -99,7 +100,7 @@ function genericPrint(path, options, printPath, args) {
decorators.push(prefix, printPath(decoratorPath), separator);
}, "decorators");
} else if (
util.isExportDeclaration(node) &&
privateUtil.isExportDeclaration(node) &&
node.declaration &&
node.declaration.decorators
) {
@ -138,7 +139,7 @@ function genericPrint(path, options, printPath, args) {
}
function hasPrettierIgnore(path) {
return util.hasIgnoreComment(path) || hasJsxIgnoreComment(path);
return privateUtil.hasIgnoreComment(path) || hasJsxIgnoreComment(path);
}
function hasJsxIgnoreComment(path) {
@ -193,7 +194,11 @@ function printPathNoParens(path, options, print, args) {
path.each(childPath => {
parts.push(print(childPath), semi, hardline);
if (
util.isNextLineEmpty(options.originalText, childPath.getValue())
sharedUtil.isNextLineEmpty(
options.originalText,
childPath.getValue(),
options
)
) {
parts.push(hardline);
}
@ -306,7 +311,8 @@ function printPathNoParens(path, options, print, args) {
parent.type === "Property";
const samePrecedenceSubExpression =
isBinaryish(n.left) && util.shouldFlatten(n.operator, n.left.operator);
isBinaryish(n.left) &&
privateUtil.shouldFlatten(n.operator, n.left.operator);
if (
shouldNotIndent ||
@ -452,9 +458,10 @@ function printPathNoParens(path, options, print, args) {
options,
/* sameIndent */ true,
comment => {
const nextCharacter = util.getNextNonSpaceNonCommentCharacterIndex(
const nextCharacter = sharedUtil.getNextNonSpaceNonCommentCharacterIndex(
options.originalText,
comment
comment,
options
);
return options.originalText.substr(nextCharacter, 2) === "=>";
}
@ -470,12 +477,12 @@ function printPathNoParens(path, options, print, args) {
// We want to always keep these types of nodes on the same line
// as the arrow.
if (
!hasLeadingOwnLineComment(options.originalText, n.body) &&
!hasLeadingOwnLineComment(options.originalText, n.body, options) &&
(n.body.type === "ArrayExpression" ||
n.body.type === "ObjectExpression" ||
n.body.type === "BlockStatement" ||
isJSXNode(n.body) ||
isTemplateOnItsOwnLine(n.body, options.originalText) ||
isTemplateOnItsOwnLine(n.body, options.originalText, options) ||
n.body.type === "ArrowFunctionExpression")
) {
return group(concat([concat(parts), " ", body]));
@ -511,7 +518,7 @@ function printPathNoParens(path, options, print, args) {
// a <= a ? a : a
const shouldAddParens =
n.body.type === "ConditionalExpression" &&
!util.startsWithNoLookaheadToken(
!privateUtil.startsWithNoLookaheadToken(
n.body,
/* forbidFunctionAndClass */ false
);
@ -689,7 +696,10 @@ function printPathNoParens(path, options, print, args) {
(n.importKind && n.importKind === "type") ||
// import {} from 'x'
/{\s*}/.test(
options.originalText.slice(util.locStart(n), util.locStart(n.source))
options.originalText.slice(
options.locStart(n),
options.locStart(n.source)
)
)
) {
parts.push("{} from ");
@ -736,7 +746,11 @@ function printPathNoParens(path, options, print, args) {
path.each(childPath => {
parts.push(indent(concat([hardline, print(childPath), semi])));
if (
util.isNextLineEmpty(options.originalText, childPath.getValue())
sharedUtil.isNextLineEmpty(
options.originalText,
childPath.getValue(),
options
)
) {
parts.push(hardline);
}
@ -810,7 +824,11 @@ function printPathNoParens(path, options, print, args) {
n.callee.type === "Import" ||
// Template literals as single arguments
(n.arguments.length === 1 &&
isTemplateOnItsOwnLine(n.arguments[0], options.originalText)) ||
isTemplateOnItsOwnLine(
n.arguments[0],
options.originalText,
options
)) ||
// Keep test declarations on a single line
// e.g. `it('long name', () => {`
(!isNew && isTestCall(n))
@ -879,10 +897,10 @@ function printPathNoParens(path, options, print, args) {
const shouldBreak =
n.type === "TSInterfaceBody" ||
(n.type !== "ObjectPattern" &&
util.hasNewlineInRange(
privateUtil.hasNewlineInRange(
options.originalText,
util.locStart(n),
util.locEnd(n)
options.locStart(n),
options.locEnd(n)
));
const parent = path.getParentNode(0);
const isFlowInterfaceLikeBody =
@ -926,7 +944,7 @@ function printPathNoParens(path, options, print, args) {
propsAndLoc.push({
node: node,
printed: print(childPath),
loc: util.locStart(node)
loc: options.locStart(node)
});
}, field);
});
@ -937,24 +955,26 @@ function printPathNoParens(path, options, print, args) {
separatorParts = [separator, line];
if (
prop.node.type === "TSPropertySignature" &&
util.hasNodeIgnoreComment(prop.node)
privateUtil.hasNodeIgnoreComment(prop.node)
) {
separatorParts.shift();
}
if (util.isNextLineEmpty(options.originalText, prop.node)) {
if (
sharedUtil.isNextLineEmpty(options.originalText, prop.node, options)
) {
separatorParts.push(hardline);
}
return result;
});
const lastElem = util.getLast(n[propertiesField]);
const lastElem = privateUtil.getLast(n[propertiesField]);
const canHaveTrailingSeparator = !(
lastElem &&
(lastElem.type === "RestProperty" ||
lastElem.type === "RestElement" ||
lastElem.type === "ExperimentalRestProperty" ||
util.hasNodeIgnoreComment(lastElem))
privateUtil.hasNodeIgnoreComment(lastElem))
);
let content;
@ -1068,7 +1088,7 @@ function printPathNoParens(path, options, print, args) {
);
}
} else {
const lastElem = util.getLast(n.elements);
const lastElem = privateUtil.getLast(n.elements);
const canHaveTrailingComma = !(
lastElem && lastElem.type === "RestElement"
);
@ -1081,7 +1101,7 @@ function printPathNoParens(path, options, print, args) {
// [1,].length === 1
// [1,,].length === 2
//
// Note that util.getLast returns null if the array is empty, but
// Note that privateUtil.getLast returns null if the array is empty, but
// we already check for an empty array just above so we are safe
const needsForcedTrailingComma =
canHaveTrailingComma && lastElem === null;
@ -1154,7 +1174,7 @@ function printPathNoParens(path, options, print, args) {
case "RegExpLiteral": // Babel 6 Literal split
return printRegex(n);
case "NumericLiteral": // Babel 6 Literal split
return util.printNumber(n.extra.raw);
return privateUtil.printNumber(n.extra.raw);
case "BooleanLiteral": // Babel 6 Literal split
case "StringLiteral": // Babel 6 Literal split
case "Literal": {
@ -1162,7 +1182,7 @@ function printPathNoParens(path, options, print, args) {
return printRegex(n.regex);
}
if (typeof n.value === "number") {
return util.printNumber(n.raw);
return privateUtil.printNumber(n.raw);
}
if (typeof n.value !== "string") {
return "" + n.value;
@ -1588,7 +1608,11 @@ function printPathNoParens(path, options, print, args) {
return concat([
casePath.call(print),
n.cases.indexOf(caseNode) !== n.cases.length - 1 &&
util.isNextLineEmpty(options.originalText, caseNode)
sharedUtil.isNextLineEmpty(
options.originalText,
caseNode,
options
)
? hardline
: ""
]);
@ -1775,7 +1799,8 @@ function printPathNoParens(path, options, print, args) {
}
const lastAttrHasTrailingComments =
n.attributes.length && hasTrailingComment(util.getLast(n.attributes));
n.attributes.length &&
hasTrailingComment(privateUtil.getLast(n.attributes));
const bracketSameLine =
options.jsxBracketSameLine &&
@ -1813,7 +1838,7 @@ function printPathNoParens(path, options, print, args) {
case "TSJsxClosingFragment": {
const hasComment = n.comments && n.comments.length;
const hasOwnLineComment =
hasComment && !n.comments.every(util.isBlockComment);
hasComment && !n.comments.every(privateUtil.isBlockComment);
const isOpeningFragment =
n.type === "JSXOpeningFragment" || n.type === "TSJsxOpeningFragment";
return concat([
@ -1835,7 +1860,7 @@ function printPathNoParens(path, options, print, args) {
throw new Error("JSXTest should be handled by JSXElement");
case "JSXEmptyExpression": {
const requiresHardline =
n.comments && !n.comments.every(util.isBlockComment);
n.comments && !n.comments.every(privateUtil.isBlockComment);
return concat([
comments.printDanglingComments(
@ -1948,7 +1973,7 @@ function printPathNoParens(path, options, print, args) {
// expression inside at the beginning of ${ instead of the beginning
// of the `.
const tabWidth = options.tabWidth;
const indentSize = util.getIndentSize(
const indentSize = privateUtil.getIndentSize(
childPath.getValue().value.raw,
tabWidth
);
@ -2118,7 +2143,7 @@ function printPathNoParens(path, options, print, args) {
(parent.type === "ObjectTypeProperty" &&
!getFlowVariance(parent) &&
!parent.optional &&
util.locStart(parent) === util.locStart(n)) ||
options.locStart(parent) === options.locStart(n)) ||
parent.type === "ObjectTypeCallProperty" ||
(parentParentParent && parentParentParent.type === "DeclareFunction")
);
@ -2138,7 +2163,7 @@ function printPathNoParens(path, options, print, args) {
parent.type === "TSTypeAnnotation") &&
parentParent.type === "ArrowFunctionExpression";
if (isObjectTypePropertyAFunction(parent)) {
if (isObjectTypePropertyAFunction(parent, options)) {
isArrowFunctionTypeAnnotation = true;
needsColon = true;
}
@ -2269,7 +2294,7 @@ function printPathNoParens(path, options, print, args) {
!(
(parent.type === "TypeAlias" ||
parent.type === "VariableDeclarator") &&
hasLeadingOwnLineComment(options.originalText, n)
hasLeadingOwnLineComment(options.originalText, n, options)
);
// {
@ -2359,7 +2384,7 @@ function printPathNoParens(path, options, print, args) {
variance || "",
printPropertyKey(path, options, print),
printOptionalToken(path),
isFunctionNotation(n) ? "" : ": ",
isFunctionNotation(n, options) ? "" : ": ",
path.call(print, "value")
]);
}
@ -2375,9 +2400,9 @@ function printPathNoParens(path, options, print, args) {
assert.strictEqual(typeof n.value, "number");
if (n.extra != null) {
return util.printNumber(n.extra.raw);
return privateUtil.printNumber(n.extra.raw);
}
return util.printNumber(n.raw);
return privateUtil.printNumber(n.raw);
case "StringTypeAnnotation":
return "string";
@ -2787,7 +2812,10 @@ function printPathNoParens(path, options, print, args) {
n.id.type === "Identifier" &&
n.id.name === "global" &&
!/namespace|module/.test(
options.originalText.slice(util.locStart(n), util.locStart(n.id))
options.originalText.slice(
options.locStart(n),
options.locStart(n.id)
)
);
if (!isGlobalDeclaration) {
@ -2890,7 +2918,10 @@ function printStatementSequence(path, options, print) {
}
}
if (util.isNextLineEmpty(text, stmt) && !isLastStatement(stmtPath)) {
if (
sharedUtil.isNextLineEmpty(text, stmt, options) &&
!isLastStatement(stmtPath)
) {
parts.push(hardline);
}
@ -2996,8 +3027,8 @@ function couldGroupArg(arg) {
}
function shouldGroupLastArg(args) {
const lastArg = util.getLast(args);
const penultimateArg = util.getPenultimate(args);
const lastArg = privateUtil.getLast(args);
const penultimateArg = privateUtil.getPenultimate(args);
return (
!hasLeadingComment(lastArg) &&
!hasTrailingComment(lastArg) &&
@ -3044,7 +3075,7 @@ function printArgumentsList(path, options, print) {
if (index === lastArgIndex) {
// do nothing
} else if (util.isNextLineEmpty(options.originalText, arg)) {
} else if (sharedUtil.isNextLineEmpty(options.originalText, arg, options)) {
if (index === 0) {
hasEmptyLineFollowingFirstArg = true;
}
@ -3119,7 +3150,7 @@ function printArgumentsList(path, options, print) {
: concat([
"(",
concat(printedArguments.slice(0, -1)),
group(util.getLast(printedExpanded), {
group(privateUtil.getLast(printedExpanded), {
shouldBreak: true
}),
")"
@ -3162,7 +3193,9 @@ function printTypeAnnotation(path, options, print) {
const isFunctionDeclarationIdentifier =
parentNode.type === "DeclareFunction" && parentNode.id === node;
if (isFlowAnnotationComment(options.originalText, node.typeAnnotation)) {
if (
isFlowAnnotationComment(options.originalText, node.typeAnnotation, options)
) {
return concat([" /*: ", path.call(print, "typeAnnotation"), " */"]);
}
@ -3206,16 +3239,17 @@ function printFunctionParams(path, print, options, expandArg, printTypeParams) {
options,
/* sameIndent */ true,
comment =>
util.getNextNonSpaceNonCommentCharacter(
privateUtil.getNextNonSpaceNonCommentCharacter(
options.originalText,
comment
comment,
options.locEnd
) === ")"
),
")"
]);
}
const lastParam = util.getLast(fun[paramsField]);
const lastParam = privateUtil.getLast(fun[paramsField]);
// If the parent is a call with the first/last argument expansion and this is the
// params of the first/last argument, we dont want the arguments to break and instead
@ -3274,8 +3308,8 @@ function printFunctionParams(path, print, options, expandArg, printTypeParams) {
];
const isFlowShorthandWithOneArg =
(isObjectTypePropertyAFunction(parent) ||
isTypeAnnotationAFunction(parent) ||
(isObjectTypePropertyAFunction(parent, options) ||
isTypeAnnotationAFunction(parent, options) ||
parent.type === "TypeAlias" ||
parent.type === "UnionTypeAnnotation" ||
parent.type === "TSUnionType" ||
@ -3424,7 +3458,7 @@ function printReturnType(path, print, options) {
if (
n.returnType &&
isFlowAnnotationComment(options.originalText, n.returnType)
isFlowAnnotationComment(options.originalText, n.returnType, options)
) {
return concat([" /*: ", returnType, " */"]);
}
@ -3530,7 +3564,7 @@ function printExportDeclaration(path, options, print) {
}
function printFlowDeclaration(path, parts) {
const parentExportDecl = util.getParentExportDeclaration(path);
const parentExportDecl = privateUtil.getParentExportDeclaration(path);
if (parentExportDecl) {
assert.strictEqual(parentExportDecl.type, "DeclareExportDeclaration");
@ -3714,7 +3748,7 @@ function printClass(path, options, print) {
if (
n.body &&
n.body.comments &&
hasLeadingOwnLineComment(options.originalText, n.body)
hasLeadingOwnLineComment(options.originalText, n.body, options)
) {
parts.push(hardline);
} else {
@ -3786,19 +3820,24 @@ function printMemberChain(path, options, print) {
// the first group whether it is in parentheses or not
function shouldInsertEmptyLineAfter(node) {
const originalText = options.originalText;
const nextCharIndex = util.getNextNonSpaceNonCommentCharacterIndex(
const nextCharIndex = sharedUtil.getNextNonSpaceNonCommentCharacterIndex(
originalText,
node
node,
options
);
const nextChar = originalText.charAt(nextCharIndex);
// if it is cut off by a parenthesis, we only account for one typed empty
// line after that parenthesis
if (nextChar == ")") {
return util.isNextLineEmptyAfterIndex(originalText, nextCharIndex + 1);
return sharedUtil.isNextLineEmptyAfterIndex(
originalText,
nextCharIndex + 1,
options
);
}
return util.isNextLineEmpty(originalText, node);
return sharedUtil.isNextLineEmpty(originalText, node, options);
}
function rec(path) {
@ -4015,7 +4054,7 @@ function printMemberChain(path, options, print) {
// Find out the last node in the first group and check if it has an
// empty line after
const lastNodeBeforeIndent = util.getLast(
const lastNodeBeforeIndent = privateUtil.getLast(
shouldMerge ? groups.slice(1, 2)[0] : groups[0]
).node;
const shouldHaveEmptyLineBeforeIndent =
@ -4240,7 +4279,7 @@ function printJSXChildren(path, options, print, jsxWhitespace) {
let endWhitespace;
// Ends with whitespace
if (util.getLast(words) === "") {
if (privateUtil.getLast(words) === "") {
words.pop();
endWhitespace = words.pop();
}
@ -4428,7 +4467,8 @@ function printJSXElement(path, options, print) {
// Trim trailing lines (or empty strings)
while (
children.length &&
(isLineNext(util.getLast(children)) || isEmpty(util.getLast(children)))
(isLineNext(privateUtil.getLast(children)) ||
isEmpty(privateUtil.getLast(children)))
) {
children.pop();
}
@ -4600,7 +4640,7 @@ function printBinaryishExpressions(
// precedence level and should be treated as a separate group, so
// print them normally. (This doesn't hold for the `**` operator,
// which is unique in that it is right-associative.)
if (util.shouldFlatten(node.operator, node.left.operator)) {
if (privateUtil.shouldFlatten(node.operator, node.left.operator)) {
// Flatten them out by recursively calling this function.
parts = parts.concat(
path.call(
@ -4657,7 +4697,7 @@ function printBinaryishExpressions(
}
function printAssignmentRight(rightNode, printedRight, canBreak, options) {
if (hasLeadingOwnLineComment(options.originalText, rightNode)) {
if (hasLeadingOwnLineComment(options.originalText, rightNode, options)) {
return indent(concat([hardline, printedRight]));
}
@ -4716,7 +4756,7 @@ function nodeStr(node, options, isFlowOrTypeScriptDirectiveLiteral) {
const raw = rawText(node);
const isDirectiveLiteral =
isFlowOrTypeScriptDirectiveLiteral || node.type === "DirectiveLiteral";
return util.printString(raw, options, isDirectiveLiteral);
return privateUtil.printString(raw, options, isDirectiveLiteral);
}
function printRegex(node) {
@ -4747,15 +4787,16 @@ function hasTrailingComment(node) {
return node.comments && node.comments.some(comment => comment.trailing);
}
function hasLeadingOwnLineComment(text, node) {
function hasLeadingOwnLineComment(text, node, options) {
if (isJSXNode(node)) {
return util.hasNodeIgnoreComment(node);
return privateUtil.hasNodeIgnoreComment(node);
}
const res =
node.comments &&
node.comments.some(
comment => comment.leading && util.hasNewline(text, util.locEnd(comment))
comment =>
comment.leading && privateUtil.hasNewline(text, options.locEnd(comment))
);
return res;
}
@ -4775,9 +4816,9 @@ function hasNakedLeftSide(node) {
);
}
function isFlowAnnotationComment(text, typeAnnotation) {
const start = util.locStart(typeAnnotation);
const end = util.skipWhitespace(text, util.locEnd(typeAnnotation));
function isFlowAnnotationComment(text, typeAnnotation, options) {
const start = options.locStart(typeAnnotation);
const end = privateUtil.skipWhitespace(text, options.locEnd(typeAnnotation));
return text.substr(start, 2) === "/*" && text.substr(end, 2) === "*/";
}
@ -4940,7 +4981,7 @@ function classChildNeedsASIProtection(node) {
// (the leftmost leaf node) and, if it (or its parents) has any
// leadingComments, returns true (so it can be wrapped in parens).
function returnArgumentHasLeadingComment(options, argument) {
if (hasLeadingOwnLineComment(options.originalText, argument)) {
if (hasLeadingOwnLineComment(options.originalText, argument, options)) {
return true;
}
@ -4950,7 +4991,7 @@ function returnArgumentHasLeadingComment(options, argument) {
while ((newLeftMost = getLeftSide(leftMost))) {
leftMost = newLeftMost;
if (hasLeadingOwnLineComment(options.originalText, leftMost)) {
if (hasLeadingOwnLineComment(options.originalText, leftMost, options)) {
return true;
}
}
@ -4972,38 +5013,38 @@ function isMemberExpressionChain(node) {
// Hack to differentiate between the following two which have the same ast
// type T = { method: () => void };
// type T = { method(): void };
function isObjectTypePropertyAFunction(node) {
function isObjectTypePropertyAFunction(node, options) {
return (
node.type === "ObjectTypeProperty" &&
node.value.type === "FunctionTypeAnnotation" &&
!node.static &&
!isFunctionNotation(node)
!isFunctionNotation(node, options)
);
}
// TODO: This is a bad hack and we need a better way to distinguish between
// arrow functions and otherwise
function isFunctionNotation(node) {
return isGetterOrSetter(node) || sameLocStart(node, node.value);
function isFunctionNotation(node, options) {
return isGetterOrSetter(node) || sameLocStart(node, node.value, options);
}
function isGetterOrSetter(node) {
return node.kind === "get" || node.kind === "set";
}
function sameLocStart(nodeA, nodeB) {
return util.locStart(nodeA) === util.locStart(nodeB);
function sameLocStart(nodeA, nodeB, options) {
return options.locStart(nodeA) === options.locStart(nodeB);
}
// Hack to differentiate between the following two which have the same ast
// declare function f(a): void;
// var f: (a) => void;
function isTypeAnnotationAFunction(node) {
function isTypeAnnotationAFunction(node, options) {
return (
(node.type === "TypeAnnotation" || node.type === "TSTypeAnnotation") &&
node.typeAnnotation.type === "FunctionTypeAnnotation" &&
!node.static &&
!sameLocStart(node, node.typeAnnotation)
!sameLocStart(node, node.typeAnnotation, options)
);
}
@ -5013,7 +5054,7 @@ function isNodeStartingWithDeclare(node, options) {
}
return (
options.originalText
.slice(0, util.locStart(node))
.slice(0, options.locStart(node))
.match(/declare[ \t]*$/) ||
options.originalText
.slice(node.range[0], node.range[1])
@ -5083,12 +5124,12 @@ function templateLiteralHasNewLines(template) {
return template.quasis.some(quasi => quasi.value.raw.includes("\n"));
}
function isTemplateOnItsOwnLine(n, text) {
function isTemplateOnItsOwnLine(n, text, options) {
return (
((n.type === "TemplateLiteral" && templateLiteralHasNewLines(n)) ||
(n.type === "TaggedTemplateExpression" &&
templateLiteralHasNewLines(n.quasi))) &&
!util.hasNewline(text, util.locStart(n), { backwards: true })
!privateUtil.hasNewline(text, options.locStart(n), { backwards: true })
);
}
@ -5103,7 +5144,11 @@ function printArrayItems(path, options, printPath, print) {
separatorParts = [",", line];
if (
childPath.getValue() &&
util.isNextLineEmpty(options.originalText, childPath.getValue())
sharedUtil.isNextLineEmpty(
options.originalText,
childPath.getValue(),
options
)
) {
separatorParts.push(softline);
}
@ -5204,7 +5249,7 @@ function willPrintOwnComments(path) {
((parent.type === "ClassDeclaration" ||
parent.type === "ClassExpression") &&
parent.superClass === node)))) &&
!util.hasIgnoreComment(path)
!privateUtil.hasIgnoreComment(path)
);
}
@ -5233,14 +5278,16 @@ function printComment(commentPath, options) {
}
const isInsideFlowComment =
options.originalText.substr(util.locEnd(comment) - 3, 3) === "*-/";
options.originalText.substr(options.locEnd(comment) - 3, 3) === "*-/";
return "/*" + comment.value + (isInsideFlowComment ? "*-/" : "*/");
}
case "CommentLine":
case "Line":
// Print shebangs with the proper comment characters
if (options.originalText.slice(util.locStart(comment)).startsWith("#!")) {
if (
options.originalText.slice(options.locStart(comment)).startsWith("#!")
) {
return "#!" + comment.value.trimRight();
}
return "//" + comment.value.trimRight();

View File

@ -1,6 +1,6 @@
"use strict";
const util = require("../common/util");
const privateUtil = require("../common/util");
const embed = require("./embed");
const doc = require("../doc");
const docBuilders = doc.builders;
@ -46,7 +46,7 @@ function genericPrint(path, options, print) {
if (shouldRemainTheSameContent(path)) {
return concat(
util
privateUtil
.splitText(
options.originalText.slice(
node.position.start.offset,
@ -80,8 +80,8 @@ function genericPrint(path, options, print) {
.replace(
new RegExp(
[
`(^|[${util.punctuationCharRange}])(_+)`,
`(_+)([${util.punctuationCharRange}]|$)`
`(^|[${privateUtil.punctuationCharRange}])(_+)`,
`(_+)([${privateUtil.punctuationCharRange}]|$)`
].join("|"),
"g"
),
@ -113,8 +113,8 @@ function genericPrint(path, options, print) {
(prevNode &&
prevNode.type === "sentence" &&
prevNode.children.length > 0 &&
util.getLast(prevNode.children).type === "word" &&
!util.getLast(prevNode.children).hasTrailingPunctuation) ||
privateUtil.getLast(prevNode.children).type === "word" &&
!privateUtil.getLast(prevNode.children).hasTrailingPunctuation) ||
(nextNode &&
nextNode.type === "sentence" &&
nextNode.children.length > 0 &&
@ -129,7 +129,7 @@ function genericPrint(path, options, print) {
case "delete":
return concat(["~~", printChildren(path, options, print), "~~"]);
case "inlineCode": {
const backtickCount = util.getMaxContinuousCount(node.value, "`");
const backtickCount = privateUtil.getMaxContinuousCount(node.value, "`");
const style = backtickCount === 1 ? "``" : "`";
const gap = backtickCount ? " " : "";
return concat([style, gap, node.value, gap, style]);
@ -190,7 +190,10 @@ function genericPrint(path, options, print) {
// fenced code block
const styleUnit = options.__inJsTemplate ? "~" : "`";
const style = styleUnit.repeat(
Math.max(3, util.getMaxContinuousCount(node.value, styleUnit) + 1)
Math.max(
3,
privateUtil.getMaxContinuousCount(node.value, styleUnit) + 1
)
);
return concat([
style,
@ -208,7 +211,7 @@ function genericPrint(path, options, print) {
case "html": {
const parentNode = path.getParentNode();
return parentNode.type === "root" &&
util.getLast(parentNode.children) === node
privateUtil.getLast(parentNode.children) === node
? node.value.trimRight()
: node.value;
}
@ -428,7 +431,7 @@ function printTable(path, options, print) {
const columnMaxWidths = contents.reduce(
(currentWidths, rowContents) =>
currentWidths.map((width, columnIndex) =>
Math.max(width, util.getStringWidth(rowContents[columnIndex]))
Math.max(width, privateUtil.getStringWidth(rowContents[columnIndex]))
),
contents[0].map(() => 3) // minimum width = 3 (---, :--, :-:, --:)
);
@ -482,15 +485,15 @@ function printTable(path, options, print) {
}
function alignLeft(text, width) {
return concat([text, " ".repeat(width - util.getStringWidth(text))]);
return concat([text, " ".repeat(width - privateUtil.getStringWidth(text))]);
}
function alignRight(text, width) {
return concat([" ".repeat(width - util.getStringWidth(text)), text]);
return concat([" ".repeat(width - privateUtil.getStringWidth(text)), text]);
}
function alignCenter(text, width) {
const spaces = width - util.getStringWidth(text);
const spaces = width - privateUtil.getStringWidth(text);
const left = Math.floor(spaces / 2);
const right = spaces - left;
return concat([" ".repeat(left), text, " ".repeat(right)]);
@ -616,7 +619,7 @@ function shouldRemainTheSameContent(path) {
}
function normalizeDoc(doc) {
return util.mapDoc(doc, currentDoc => {
return privateUtil.mapDoc(doc, currentDoc => {
if (!currentDoc.parts) {
return currentDoc;
}
@ -668,7 +671,7 @@ function printTitle(title, options) {
function normalizeParts(parts) {
return parts.reduce((current, part) => {
const lastPart = util.getLast(current);
const lastPart = privateUtil.getLast(current);
if (typeof lastPart === "string" && typeof part === "string") {
current.splice(-1, 1, lastPart + part);
@ -699,5 +702,5 @@ module.exports = {
print: genericPrint,
embed,
massageAstNode: clean,
hasPrettierIgnore: util.hasIgnoreComment
hasPrettierIgnore: privateUtil.hasIgnoreComment
};

View File

@ -4,7 +4,6 @@ const assert = require("assert");
const comments = require("./comments");
const FastPath = require("../common/fast-path");
const multiparser = require("./multiparser");
const util = require("../common/util");
const doc = require("../doc");
const docBuilders = doc.builders;
@ -75,7 +74,10 @@ function genericPrint(path, options, printPath, args) {
// Escape hatch
if (printer.hasPrettierIgnore && printer.hasPrettierIgnore(path)) {
return options.originalText.slice(util.locStart(node), util.locEnd(node));
return options.originalText.slice(
options.locStart(node),
options.locEnd(node)
);
}
if (node) {

View File

@ -9,20 +9,17 @@ const indent = docBuilders.indent;
const lineSuffix = docBuilders.lineSuffix;
const join = docBuilders.join;
const cursor = docBuilders.cursor;
const util = require("../common/util");
const privateUtil = require("../common/util");
const sharedUtil = require("../common/util-shared");
const childNodesCacheKey = Symbol("child-nodes");
const locStart = util.locStart;
const locEnd = util.locEnd;
const getNextNonSpaceNonCommentCharacter =
util.getNextNonSpaceNonCommentCharacter;
const getNextNonSpaceNonCommentCharacterIndex =
util.getNextNonSpaceNonCommentCharacterIndex;
function getSortedChildNodes(node, text, options, resultArray) {
if (!node) {
return;
}
const printer = options.printer;
const locStart = options.locStart;
const locEnd = options.locEnd;
if (resultArray) {
if (node && printer.canAttachComment && printer.canAttachComment(node)) {
@ -82,6 +79,8 @@ function getSortedChildNodes(node, text, options, resultArray) {
// .precedingNode, .enclosingNode, and/or .followingNode properties, at
// least one of which is guaranteed to be defined.
function decorateComment(node, comment, text, options) {
const locStart = options.locStart;
const locEnd = options.locEnd;
const childNodes = getSortedChildNodes(node, text, options);
let precedingNode;
let followingNode;
@ -134,17 +133,23 @@ function decorateComment(node, comment, text, options) {
comment.enclosingNode.type === "TemplateLiteral"
) {
const quasis = comment.enclosingNode.quasis;
const commentIndex = findExpressionIndexForComment(quasis, comment);
const commentIndex = findExpressionIndexForComment(
quasis,
comment,
options
);
if (
precedingNode &&
findExpressionIndexForComment(quasis, precedingNode) !== commentIndex
findExpressionIndexForComment(quasis, precedingNode, options) !==
commentIndex
) {
precedingNode = null;
}
if (
followingNode &&
findExpressionIndexForComment(quasis, followingNode) !== commentIndex
findExpressionIndexForComment(quasis, followingNode, options) !==
commentIndex
) {
followingNode = null;
}
@ -165,6 +170,8 @@ function attach(comments, ast, text, options) {
}
const tiesToBreak = [];
const locStart = options.locStart;
const locEnd = options.locEnd;
comments.forEach((comment, i) => {
if (options.parser === "json" && locStart(comment) - locStart(ast) <= 0) {
@ -180,7 +187,7 @@ function attach(comments, ast, text, options) {
const isLastComment = comments.length - 1 === i;
if (util.hasNewline(text, locStart(comment), { backwards: true })) {
if (privateUtil.hasNewline(text, locStart(comment), { backwards: true })) {
// If a comment exists on its own line, prefer a leading comment.
// We also need to check if it's the first line of the file.
if (
@ -189,7 +196,8 @@ function attach(comments, ast, text, options) {
precedingNode,
enclosingNode,
followingNode,
comment
comment,
options
) ||
handleMemberExpressionComments(enclosingNode, followingNode, comment) ||
handleIfStatementComments(
@ -197,7 +205,8 @@ function attach(comments, ast, text, options) {
precedingNode,
enclosingNode,
followingNode,
comment
comment,
options
) ||
handleTryStatementComments(enclosingNode, followingNode, comment) ||
handleClassComments(
@ -219,10 +228,17 @@ function attach(comments, ast, text, options) {
text,
enclosingNode,
precedingNode,
comment
comment,
options
) ||
handleAssignmentPatternComments(enclosingNode, comment) ||
handleMethodNameComments(text, enclosingNode, precedingNode, comment)
handleMethodNameComments(
text,
enclosingNode,
precedingNode,
comment,
options
)
) {
// We're good
} else if (followingNode) {
@ -237,21 +253,23 @@ function attach(comments, ast, text, options) {
/* istanbul ignore next */
addDanglingComment(ast, comment);
}
} else if (util.hasNewline(text, locEnd(comment))) {
} else if (privateUtil.hasNewline(text, locEnd(comment))) {
if (
handleLastFunctionArgComments(
text,
precedingNode,
enclosingNode,
followingNode,
comment
comment,
options
) ||
handleConditionalExpressionComments(
enclosingNode,
precedingNode,
followingNode,
comment,
text
text,
options
) ||
handleImportSpecifierComments(enclosingNode, comment) ||
handleIfStatementComments(
@ -259,7 +277,8 @@ function attach(comments, ast, text, options) {
precedingNode,
enclosingNode,
followingNode,
comment
comment,
options
) ||
handleClassComments(
enclosingNode,
@ -296,14 +315,27 @@ function attach(comments, ast, text, options) {
precedingNode,
enclosingNode,
followingNode,
comment
comment,
options
) ||
handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) ||
handleCommentInEmptyParens(text, enclosingNode, comment) ||
handleMethodNameComments(text, enclosingNode, precedingNode, comment) ||
handleCommentInEmptyParens(text, enclosingNode, comment, options) ||
handleMethodNameComments(
text,
enclosingNode,
precedingNode,
comment,
options
) ||
handleOnlyComments(enclosingNode, ast, comment, isLastComment) ||
handleCommentAfterArrowParams(text, enclosingNode, comment) ||
handleFunctionNameComments(text, enclosingNode, precedingNode, comment)
handleCommentAfterArrowParams(text, enclosingNode, comment, options) ||
handleFunctionNameComments(
text,
enclosingNode,
precedingNode,
comment,
options
)
) {
// We're good
} else if (precedingNode && followingNode) {
@ -316,7 +348,7 @@ function attach(comments, ast, text, options) {
if (tieCount > 0) {
const lastTie = tiesToBreak[tieCount - 1];
if (lastTie.followingNode !== comment.followingNode) {
breakTies(tiesToBreak, text);
breakTies(tiesToBreak, text, options);
}
}
tiesToBreak.push(comment);
@ -334,7 +366,7 @@ function attach(comments, ast, text, options) {
}
});
breakTies(tiesToBreak, text);
breakTies(tiesToBreak, text, options);
comments.forEach(comment => {
// These node references were useful for breaking ties, but we
@ -346,7 +378,7 @@ function attach(comments, ast, text, options) {
});
}
function breakTies(tiesToBreak, text) {
function breakTies(tiesToBreak, text, options) {
const tieCount = tiesToBreak.length;
if (tieCount === 0) {
return;
@ -354,7 +386,7 @@ function breakTies(tiesToBreak, text) {
const precedingNode = tiesToBreak[0].precedingNode;
const followingNode = tiesToBreak[0].followingNode;
let gapEndPos = locStart(followingNode);
let gapEndPos = options.locStart(followingNode);
// Iterate backwards through tiesToBreak, examining the gaps
// between the tied comments. In order to qualify as leading, a
@ -371,9 +403,9 @@ function breakTies(tiesToBreak, text) {
assert.strictEqual(comment.precedingNode, precedingNode);
assert.strictEqual(comment.followingNode, followingNode);
const gap = text.slice(locEnd(comment), gapEndPos).trim();
const gap = text.slice(options.locEnd(comment), gapEndPos).trim();
if (gap === "" || /^\(+$/.test(gap)) {
gapEndPos = locStart(comment);
gapEndPos = options.locStart(comment);
} else {
// The gap string contained something other than whitespace or open
// parentheses.
@ -461,7 +493,8 @@ function handleIfStatementComments(
precedingNode,
enclosingNode,
followingNode,
comment
comment,
options
) {
if (
!enclosingNode ||
@ -476,7 +509,11 @@ function handleIfStatementComments(
// if (a /* comment */) {}
// The only workaround I found is to look at the next character to see if
// it is a ).
const nextCharacter = getNextNonSpaceNonCommentCharacter(text, comment);
const nextCharacter = privateUtil.getNextNonSpaceNonCommentCharacter(
text,
comment,
options.locEnd
);
if (nextCharacter === ")") {
addTrailingComment(precedingNode, comment);
return true;
@ -552,11 +589,16 @@ function handleConditionalExpressionComments(
precedingNode,
followingNode,
comment,
text
text,
options
) {
const isSameLineAsPrecedingNode =
precedingNode &&
!util.hasNewlineInRange(text, locEnd(precedingNode), locStart(comment));
!privateUtil.hasNewlineInRange(
text,
options.locEnd(precedingNode),
options.locStart(comment)
);
if (
(!precedingNode || !isSameLineAsPrecedingNode) &&
@ -611,7 +653,13 @@ function handleClassComments(
return false;
}
function handleMethodNameComments(text, enclosingNode, precedingNode, comment) {
function handleMethodNameComments(
text,
enclosingNode,
precedingNode,
comment,
options
) {
// This is only needed for estree parsers (flow, typescript) to attach
// after a method name:
// obj = { fn /*comment*/() {} };
@ -624,7 +672,11 @@ function handleMethodNameComments(text, enclosingNode, precedingNode, comment) {
enclosingNode.key === precedingNode &&
// special Property case: { key: /*comment*/(value) };
// comment should be attached to value instead of key
getNextNonSpaceNonCommentCharacter(text, precedingNode) !== ":"
privateUtil.getNextNonSpaceNonCommentCharacter(
text,
precedingNode,
options.locEnd
) !== ":"
) {
addTrailingComment(precedingNode, comment);
return true;
@ -653,9 +705,16 @@ function handleFunctionNameComments(
text,
enclosingNode,
precedingNode,
comment
comment,
options
) {
if (getNextNonSpaceNonCommentCharacter(text, comment) !== "(") {
if (
privateUtil.getNextNonSpaceNonCommentCharacter(
text,
comment,
options.locEnd
) !== "("
) {
return false;
}
@ -674,12 +733,16 @@ function handleFunctionNameComments(
return false;
}
function handleCommentAfterArrowParams(text, enclosingNode, comment) {
function handleCommentAfterArrowParams(text, enclosingNode, comment, options) {
if (!(enclosingNode && enclosingNode.type === "ArrowFunctionExpression")) {
return false;
}
const index = getNextNonSpaceNonCommentCharacterIndex(text, comment);
const index = sharedUtil.getNextNonSpaceNonCommentCharacterIndex(
text,
comment,
options
);
if (text.substr(index, 2) === "=>") {
addDanglingComment(enclosingNode, comment);
return true;
@ -688,8 +751,14 @@ function handleCommentAfterArrowParams(text, enclosingNode, comment) {
return false;
}
function handleCommentInEmptyParens(text, enclosingNode, comment) {
if (getNextNonSpaceNonCommentCharacter(text, comment) !== ")") {
function handleCommentInEmptyParens(text, enclosingNode, comment, options) {
if (
privateUtil.getNextNonSpaceNonCommentCharacter(
text,
comment,
options.locEnd
) !== ")"
) {
return false;
}
@ -727,7 +796,8 @@ function handleLastFunctionArgComments(
precedingNode,
enclosingNode,
followingNode,
comment
comment,
options
) {
// Type definitions functions
if (
@ -753,7 +823,11 @@ function handleLastFunctionArgComments(
enclosingNode.type === "FunctionDeclaration" ||
enclosingNode.type === "ObjectMethod" ||
enclosingNode.type === "ClassMethod") &&
getNextNonSpaceNonCommentCharacter(text, comment) === ")"
privateUtil.getNextNonSpaceNonCommentCharacter(
text,
comment,
options.locEnd
) === ")"
) {
addTrailingComment(precedingNode, comment);
return true;
@ -870,13 +944,14 @@ function handleImportDeclarationComments(
text,
enclosingNode,
precedingNode,
comment
comment,
options
) {
if (
precedingNode &&
enclosingNode &&
enclosingNode.type === "ImportDeclaration" &&
util.hasNewline(text, util.locEnd(comment))
privateUtil.hasNewline(text, options.locEnd(comment))
) {
addTrailingComment(precedingNode, comment);
return true;
@ -924,8 +999,8 @@ function printComment(commentPath, options) {
return options.printer.printComment(commentPath, options);
}
function findExpressionIndexForComment(quasis, comment) {
const startPos = locStart(comment) - 1;
function findExpressionIndexForComment(quasis, comment, options) {
const startPos = options.locStart(comment) - 1;
for (let i = 1; i < quasis.length; ++i) {
if (startPos < getQuasiRange(quasis[i]).start) {
@ -954,14 +1029,16 @@ function printLeadingComment(commentPath, print, options) {
if (!contents) {
return "";
}
const isBlock = util.isBlockComment(comment);
const isBlock = privateUtil.isBlockComment(comment);
// Leading block comments should see if they need to stay on the
// same line or not.
if (isBlock) {
return concat([
contents,
util.hasNewline(options.originalText, locEnd(comment)) ? hardline : " "
privateUtil.hasNewline(options.originalText, options.locEnd(comment))
? hardline
: " "
]);
}
@ -974,7 +1051,7 @@ function printTrailingComment(commentPath, print, options) {
if (!contents) {
return "";
}
const isBlock = util.isBlockComment(comment);
const isBlock = privateUtil.isBlockComment(comment);
// We don't want the line to break
// when the parentParentNode is a ClassDeclaration/-Expression
@ -988,7 +1065,7 @@ function printTrailingComment(commentPath, print, options) {
parentParentNode.superClass === parentNode;
if (
util.hasNewline(options.originalText, locStart(comment), {
privateUtil.hasNewline(options.originalText, options.locStart(comment), {
backwards: true
})
) {
@ -1004,9 +1081,10 @@ function printTrailingComment(commentPath, print, options) {
// if this a comment on its own line; normal trailing comments are
// always at the end of another expression.
const isLineBeforeEmpty = util.isPreviousLineEmpty(
const isLineBeforeEmpty = privateUtil.isPreviousLineEmpty(
options.originalText,
comment
comment,
options.locStart
);
return lineSuffix(
@ -1082,7 +1160,12 @@ function printComments(path, print, options, needsSemi) {
leadingParts.push(contents);
const text = options.originalText;
if (util.hasNewline(text, util.skipNewline(text, util.locEnd(comment)))) {
if (
privateUtil.hasNewline(
text,
privateUtil.skipNewline(text, options.locEnd(comment))
)
) {
leadingParts.push(hardline);
}
} else if (trailing) {

View File

@ -9,7 +9,9 @@ const getPrinter = require("./get-printer");
const hiddenDefaults = {
astFormat: "estree",
printer: {}
printer: {},
locStart: null,
locEnd: null
};
// Copy options and fill in default values.
@ -47,7 +49,10 @@ function normalize(options, opts) {
}
}
rawOptions.astFormat = resolveParser(rawOptions).astFormat;
const parser = resolveParser(rawOptions);
rawOptions.astFormat = parser.astFormat;
rawOptions.locEnd = parser.locEnd;
rawOptions.locStart = parser.locStart;
rawOptions.printer = getPrinter(rawOptions);
Object.keys(defaults).forEach(k => {

View File

@ -2,6 +2,10 @@
const path = require("path");
const ConfigError = require("../common/errors").ConfigError;
const js = require("../language-js/index.js");
const locStart = js.locStart;
const locEnd = js.locEnd;
function getParsers(options) {
return options.plugins.reduce(
@ -17,7 +21,9 @@ function resolveParser(opts, parsers) {
// Custom parser API always works with JavaScript.
return {
parse: opts.parser,
astFormat: "estree"
astFormat: "estree",
locStart,
locEnd
};
}
@ -28,7 +34,9 @@ function resolveParser(opts, parsers) {
try {
return {
parse: eval("require")(path.resolve(process.cwd(), opts.parser)),
astFormat: "estree"
astFormat: "estree",
locStart,
locEnd
};
} catch (err) {
/* istanbul ignore next */

View File

@ -0,0 +1,13 @@
"use strict";
const sharedUtil = require("../../src/common/util-shared");
test("shared util has correct structure", () => {
expect(typeof sharedUtil.isNextLineEmpty).toEqual("function");
expect(typeof sharedUtil.isNextLineEmptyAfterIndex).toEqual("function");
expect(typeof sharedUtil.getNextNonSpaceNonCommentCharacterIndex).toEqual(
"function"
);
expect(typeof sharedUtil.mapDoc).toEqual("function");
expect(typeof sharedUtil.makeString).toEqual("function");
});