diff --git a/index.js b/index.js index e27d98730..cec86f70e 100644 --- a/index.js +++ b/index.js @@ -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, diff --git a/src/common/fast-path.js b/src/common/fast-path.js index 6e047acd4..97e102520 100644 --- a/src/common/fast-path.js +++ b/src/common/fast-path.js @@ -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); diff --git a/src/common/util-shared.js b/src/common/util-shared.js new file mode 100644 index 000000000..da3859594 --- /dev/null +++ b/src/common/util-shared.js @@ -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 +}; diff --git a/src/common/util.js b/src/common/util.js index dd8564483..e634dc3fc 100644 --- a/src/common/util.js +++ b/src/common/util.js @@ -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 }; diff --git a/src/language-css/index.js b/src/language-css/index.js index debe15489..07008c877 100644 --- a/src/language-css/index.js +++ b/src/language-css/index.js @@ -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. diff --git a/src/language-css/printer-postcss.js b/src/language-css/printer-postcss.js index 85e048c50..b96857f06 100644 --- a/src/language-css/printer-postcss.js +++ b/src/language-css/printer-postcss.js @@ -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 }; diff --git a/src/language-graphql/index.js b/src/language-graphql/index.js index 4f974bd22..24b05ebd5 100644 --- a/src/language-graphql/index.js +++ b/src/language-graphql/index.js @@ -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; + } } }; diff --git a/src/language-graphql/printer-graphql.js b/src/language-graphql/printer-graphql.js index 1311a8e54..88b9eca1b 100644 --- a/src/language-graphql/printer-graphql.js +++ b/src/language-graphql/printer-graphql.js @@ -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 }; diff --git a/src/language-handlebars/index.js b/src/language-handlebars/index.js index 8278df182..59e0bb48b 100644 --- a/src/language-handlebars/index.js +++ b/src/language-handlebars/index.js @@ -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; + } } }; diff --git a/src/language-html/embed.js b/src/language-html/embed.js index 8c1f7a6e8..c07a6e97d 100644 --- a/src/language-html/embed.js +++ b/src/language-html/embed.js @@ -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; diff --git a/src/language-html/index.js b/src/language-html/index.js index 0842539d5..2e8b1d21b 100644 --- a/src/language-html/index.js +++ b/src/language-html/index.js @@ -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; + } } }; diff --git a/src/language-html/printer-htmlparser2.js b/src/language-html/printer-htmlparser2.js index cd85709d0..3da2c65f7 100644 --- a/src/language-html/printer-htmlparser2.js +++ b/src/language-html/printer-htmlparser2.js @@ -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 }; diff --git a/src/language-js/index.js b/src/language-js/index.js index 78c141a19..527158307 100644 --- a/src/language-js/index.js +++ b/src/language-js/index.js @@ -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 }; diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index c2d94ce75..f5e8a704e 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -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(); diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index 6e92696c6..2072eb2c7 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -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 }; diff --git a/src/main/ast-to-doc.js b/src/main/ast-to-doc.js index 7ebaa5401..c15bd4339 100644 --- a/src/main/ast-to-doc.js +++ b/src/main/ast-to-doc.js @@ -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) { diff --git a/src/main/comments.js b/src/main/comments.js index 5273a46a0..2cc61cb31 100644 --- a/src/main/comments.js +++ b/src/main/comments.js @@ -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) { diff --git a/src/main/options.js b/src/main/options.js index 69691e858..7c331b01e 100644 --- a/src/main/options.js +++ b/src/main/options.js @@ -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 => { diff --git a/src/main/parser.js b/src/main/parser.js index 80e01eff2..8452be296 100644 --- a/src/main/parser.js +++ b/src/main/parser.js @@ -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 */ diff --git a/tests_integration/__tests__/util-shared.js b/tests_integration/__tests__/util-shared.js new file mode 100644 index 000000000..89952ebb3 --- /dev/null +++ b/tests_integration/__tests__/util-shared.js @@ -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"); +});