UNPKG

prettier-plugin-solidity

Version:

A Prettier Plugin for automatically formatting your Solidity code.

503 lines 20.7 kB
import { util } from "prettier"; import { getNextNonSpaceNonCommentCharacter } from "../../common/backward-compatibility.js"; const { addLeadingComment, addTrailingComment, addDanglingComment, hasNewline, hasNewlineInRange } = util; export function handleOwnLineComment(comment, text, options, ast, isLastComment) { const { precedingNode, enclosingNode, followingNode } = comment; if (handleLastFunctionArgComments(text, precedingNode, enclosingNode, followingNode, comment, options) || handleMemberExpressionComments(enclosingNode, followingNode, comment) || handleIfStatementComments(text, precedingNode, enclosingNode, followingNode, comment, options) || handleWhileComments(text, precedingNode, enclosingNode, followingNode, comment, options) || handleTryStatementComments(enclosingNode, precedingNode, followingNode, comment) || handleClassComments(enclosingNode, precedingNode, followingNode, comment) || handleImportSpecifierComments(enclosingNode, comment) || handleForComments(enclosingNode, precedingNode, comment) || handleUnionTypeComments(precedingNode, enclosingNode, followingNode, comment) || handleOnlyComments(enclosingNode, ast, comment, isLastComment) || handleImportDeclarationComments(text, enclosingNode, precedingNode, comment, options) || handleAssignmentPatternComments(enclosingNode, comment) || handleMethodNameComments(text, enclosingNode, precedingNode, comment, options)) { return true; } return false; } export function handleEndOfLineComment(comment, text, options, ast, isLastComment) { const { precedingNode, enclosingNode, followingNode } = comment; if (handleLastFunctionArgComments(text, precedingNode, enclosingNode, followingNode, comment, options) || handleConditionalExpressionComments(enclosingNode, precedingNode, followingNode, comment, text, options) || handleImportSpecifierComments(enclosingNode, comment) || handleIfStatementComments(text, precedingNode, enclosingNode, followingNode, comment, options) || handleWhileComments(text, precedingNode, enclosingNode, followingNode, comment, options) || handleTryStatementComments(enclosingNode, precedingNode, followingNode, comment) || handleClassComments(enclosingNode, precedingNode, followingNode, comment) || handleLabeledStatementComments(enclosingNode, comment) || handleCallExpressionComments(precedingNode, enclosingNode, comment) || handlePropertyComments(enclosingNode, comment) || handleOnlyComments(enclosingNode, ast, comment, isLastComment) || handleTypeAliasComments(enclosingNode, followingNode, comment) || handleVariableDeclaratorComments(enclosingNode, followingNode, comment)) { return true; } return false; } export function handleRemainingComment(comment, text, options, ast, isLastComment) { const { precedingNode, enclosingNode, followingNode } = comment; if (handleIfStatementComments(text, precedingNode, enclosingNode, followingNode, comment, options) || handleWhileComments(text, precedingNode, enclosingNode, followingNode, comment, options) || handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) || handleCommentInEmptyParens(text, enclosingNode, comment, options) || handleMethodNameComments(text, enclosingNode, precedingNode, comment, options) || handleOnlyComments(enclosingNode, ast, comment, isLastComment) || handleCommentAfterArrowParams(text, enclosingNode, comment, options) || handleFunctionNameComments(text, enclosingNode, precedingNode, comment, options) || handleTSMappedTypeComments(text, enclosingNode, precedingNode, followingNode, comment) || handleBreakAndContinueStatementComments(enclosingNode, comment)) { return true; } return false; } function addBlockStatementFirstComment(node, comment) { if (!node.body) { addDanglingComment(node, comment); return; } const body = node.body.filter(n => n.type !== "EmptyStatement"); if (body.length === 0) { addDanglingComment(node, comment); } else { addLeadingComment(body[0], comment); } } function addBlockOrNotComment(node, comment) { if (node.type === "BlockStatement") { addBlockStatementFirstComment(node, comment); } else { addLeadingComment(node, comment); } } // There are often comments before the else clause of if statements like // // if (1) { ... } // // comment // else { ... } // // They are being attached as leading comments of the BlockExpression which // is not well printed. What we want is to instead move the comment inside // of the block and make it leadingComment of the first element of the block // or dangling comment of the block if there is nothing inside // // if (1) { ... } // else { // // comment // ... // } function handleIfStatementComments(text, precedingNode, enclosingNode, followingNode, comment, options) { if (!enclosingNode || enclosingNode.type !== "IfStatement" || !followingNode) { return false; } // We unfortunately have no way using the AST or location of nodes to know // if the comment is positioned before the condition parenthesis: // 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, options.locEnd); if (nextCharacter === ")") { addTrailingComment(precedingNode, comment); return true; } // Comments before `else`: // - treat as trailing comments of the trueBody, if it's a ExpressionStatement // - treat as a dangling comment otherwise if (precedingNode === enclosingNode.trueBody && followingNode === enclosingNode.falseBody) { if (precedingNode.type === "ExpressionStatement") { addTrailingComment(precedingNode, comment); } else { addDanglingComment(enclosingNode, comment); } return true; } if (followingNode.type === "ExpressionStatement") { addBlockStatementFirstComment(followingNode, comment); return true; } if (followingNode.type === "IfStatement") { addBlockOrNotComment(followingNode.trueBody, comment); return true; } // For comments positioned after the condition parenthesis in an if statement // before the trueBody without brackets on, such as // if (a) /* comment */ true, // we look at the next character to see if the following node // is the trueBody for the if statement if (enclosingNode.trueBody === followingNode) { addLeadingComment(followingNode, comment); return true; } return false; } function handleWhileComments(text, precedingNode, enclosingNode, followingNode, comment, options) { if (!enclosingNode || enclosingNode.type !== "WhileStatement" || !followingNode) { return false; } // We unfortunately have no way using the AST or location of nodes to know // if the comment is positioned before the condition parenthesis: // while (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, options.locEnd); if (nextCharacter === ")") { addTrailingComment(precedingNode, comment); return true; } if (followingNode.type === "BlockStatement") { addBlockStatementFirstComment(followingNode, comment); return true; } return false; } // Same as IfStatement but for TryStatement function handleTryStatementComments(enclosingNode, precedingNode, followingNode, comment) { if (!enclosingNode || (enclosingNode.type !== "TryStatement" && enclosingNode.type !== "CatchClause") || !followingNode) { return false; } if (enclosingNode.type === "CatchClause" && precedingNode) { addTrailingComment(precedingNode, comment); return true; } if (followingNode.type === "BlockStatement") { addBlockStatementFirstComment(followingNode, comment); return true; } if (followingNode.type === "TryStatement") { addBlockOrNotComment(followingNode.finalizer, comment); return true; } if (followingNode.type === "CatchClause") { addBlockOrNotComment(followingNode.body, comment); return true; } return false; } function handleMemberExpressionComments(enclosingNode, followingNode, comment) { if (enclosingNode && enclosingNode.type === "MemberExpression" && followingNode && followingNode.type === "Identifier") { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleConditionalExpressionComments(enclosingNode, precedingNode, followingNode, comment, text, options) { const isSameLineAsPrecedingNode = precedingNode && !hasNewlineInRange(text, options.locEnd(precedingNode), options.locStart(comment)); if ((!precedingNode || !isSameLineAsPrecedingNode) && enclosingNode && enclosingNode.type === "ConditionalExpression" && followingNode) { addLeadingComment(followingNode, comment); return true; } return false; } function handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) { if (enclosingNode && (enclosingNode.type === "ObjectProperty" || enclosingNode.type === "Property") && enclosingNode.shorthand && enclosingNode.key === precedingNode && enclosingNode.value.type === "AssignmentPattern") { addTrailingComment(enclosingNode.value.left, comment); return true; } return false; } function handleClassComments(enclosingNode, precedingNode, followingNode, comment) { if (enclosingNode && (enclosingNode.type === "ClassDeclaration" || enclosingNode.type === "ClassExpression") && (enclosingNode.decorators && enclosingNode.decorators.length > 0) && !(followingNode && followingNode.type === "Decorator")) { if (!enclosingNode.decorators || enclosingNode.decorators.length === 0) { addLeadingComment(enclosingNode, comment); } else { addTrailingComment(enclosingNode.decorators[enclosingNode.decorators.length - 1], comment); } return true; } return false; } 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*/() {} }; if (enclosingNode && precedingNode && (enclosingNode.type === "Property" || enclosingNode.type === "MethodDefinition") && precedingNode.type === "Identifier" && enclosingNode.key === precedingNode && // special Property case: { key: /*comment*/(value) }; // comment should be attached to value instead of key getNextNonSpaceNonCommentCharacter(text, precedingNode, options.locEnd) !== ":") { addTrailingComment(precedingNode, comment); return true; } // Print comments between decorators and class methods as a trailing comment // on the decorator node instead of the method node if (precedingNode && enclosingNode && precedingNode.type === "Decorator" && (enclosingNode.type === "ClassMethod" || enclosingNode.type === "ClassProperty" || enclosingNode.type === "TSAbstractClassProperty" || enclosingNode.type === "TSAbstractMethodDefinition" || enclosingNode.type === "MethodDefinition")) { addTrailingComment(precedingNode, comment); return true; } return false; } function handleFunctionNameComments(text, enclosingNode, precedingNode, comment, options) { if (getNextNonSpaceNonCommentCharacter(text, comment, options.locEnd) !== "(") { return false; } if (precedingNode && enclosingNode && (enclosingNode.type === "FunctionDeclaration" || enclosingNode.type === "FunctionExpression" || enclosingNode.type === "ClassMethod" || enclosingNode.type === "MethodDefinition" || enclosingNode.type === "ObjectMethod")) { addTrailingComment(precedingNode, comment); return true; } return false; } function handleCommentAfterArrowParams(text, enclosingNode, comment, options) { if (!(enclosingNode && enclosingNode.type === "ArrowFunctionExpression")) { return false; } const index = getNextNonSpaceNonCommentCharacterIndex(text, comment, options); if (text.substr(index, 2) === "=>") { addDanglingComment(enclosingNode, comment); return true; } return false; } function handleCommentInEmptyParens(text, enclosingNode, comment, options) { if (getNextNonSpaceNonCommentCharacter(text, comment, options.locEnd) !== ")") { return false; } // Only add dangling comments to fix the case when no params are present, // i.e. a function without any argument. if (enclosingNode && (((enclosingNode.type === "FunctionDeclaration" || enclosingNode.type === "FunctionExpression" || (enclosingNode.type === "ArrowFunctionExpression" && (enclosingNode.body.type !== "CallExpression" || enclosingNode.body.arguments.length === 0)) || enclosingNode.type === "ClassMethod" || enclosingNode.type === "ObjectMethod") && enclosingNode.params.length === 0) || ((enclosingNode.type === "CallExpression" || enclosingNode.type === "NewExpression") && enclosingNode.arguments.length === 0))) { addDanglingComment(enclosingNode, comment); return true; } if (enclosingNode && (enclosingNode.type === "MethodDefinition" && enclosingNode.value.params.length === 0)) { addDanglingComment(enclosingNode.value, comment); return true; } return false; } function handleLastFunctionArgComments(text, precedingNode, enclosingNode, followingNode, comment, options) { // Type definitions functions if (precedingNode && precedingNode.type === "FunctionTypeParam" && enclosingNode && enclosingNode.type === "FunctionTypeAnnotation" && followingNode && followingNode.type !== "FunctionTypeParam") { addTrailingComment(precedingNode, comment); return true; } // Real functions if (precedingNode && (precedingNode.type === "Identifier" || precedingNode.type === "AssignmentPattern") && enclosingNode && (enclosingNode.type === "ArrowFunctionExpression" || enclosingNode.type === "FunctionExpression" || enclosingNode.type === "FunctionDeclaration" || enclosingNode.type === "ObjectMethod" || enclosingNode.type === "ClassMethod") && getNextNonSpaceNonCommentCharacter(text, comment, options.locEnd) === ")") { addTrailingComment(precedingNode, comment); return true; } return false; } function handleImportSpecifierComments(enclosingNode, comment) { if (enclosingNode && enclosingNode.type === "ImportSpecifier") { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleLabeledStatementComments(enclosingNode, comment) { if (enclosingNode && enclosingNode.type === "LabeledStatement") { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleBreakAndContinueStatementComments(enclosingNode, comment) { if (enclosingNode && (enclosingNode.type === "ContinueStatement" || enclosingNode.type === "BreakStatement") && !enclosingNode.label) { addTrailingComment(enclosingNode, comment); return true; } return false; } function handleCallExpressionComments(precedingNode, enclosingNode, comment) { if (enclosingNode && enclosingNode.type === "CallExpression" && precedingNode && enclosingNode.callee === precedingNode && enclosingNode.arguments.length > 0) { addLeadingComment(enclosingNode.arguments[0], comment); return true; } return false; } function handleUnionTypeComments(precedingNode, enclosingNode, followingNode, comment) { if (enclosingNode && (enclosingNode.type === "UnionTypeAnnotation" || enclosingNode.type === "TSUnionType")) { addTrailingComment(precedingNode, comment); return true; } return false; } function handlePropertyComments(enclosingNode, comment) { if (enclosingNode && (enclosingNode.type === "Property" || enclosingNode.type === "ObjectProperty")) { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleOnlyComments(enclosingNode, ast, comment, isLastComment) { // With Flow the enclosingNode is undefined so use the AST instead. if (ast && ast.body && ast.body.length === 0) { if (isLastComment) { addDanglingComment(ast, comment); } else { addLeadingComment(ast, comment); } return true; } else if (enclosingNode && enclosingNode.type === "Program" && enclosingNode.body.length === 0 && enclosingNode.directives && enclosingNode.directives.length === 0) { if (isLastComment) { addDanglingComment(enclosingNode, comment); } else { addLeadingComment(enclosingNode, comment); } return true; } return false; } function handleForComments(enclosingNode, precedingNode, comment) { if (enclosingNode && (enclosingNode.type === "ForInStatement" || enclosingNode.type === "ForOfStatement")) { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleImportDeclarationComments(text, enclosingNode, precedingNode, comment, options) { if (precedingNode && precedingNode.type === "ImportSpecifier" && enclosingNode && enclosingNode.type === "ImportDeclaration" && hasNewline(text, options.locEnd(comment))) { addTrailingComment(precedingNode, comment); return true; } return false; } function handleAssignmentPatternComments(enclosingNode, comment) { if (enclosingNode && enclosingNode.type === "AssignmentPattern") { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleTypeAliasComments(enclosingNode, followingNode, comment) { if (enclosingNode && enclosingNode.type === "TypeAlias") { addLeadingComment(enclosingNode, comment); return true; } return false; } function handleVariableDeclaratorComments(enclosingNode, followingNode, comment) { if (enclosingNode && (enclosingNode.type === "VariableDeclarator" || enclosingNode.type === "AssignmentExpression") && followingNode && (followingNode.type === "ObjectExpression" || followingNode.type === "ArrayExpression" || followingNode.type === "TemplateLiteral" || followingNode.type === "TaggedTemplateExpression")) { addLeadingComment(followingNode, comment); return true; } return false; } function handleTSMappedTypeComments(text, enclosingNode, precedingNode, followingNode, comment) { if (!enclosingNode || enclosingNode.type !== "TSMappedType") { return false; } if (followingNode && followingNode.type === "TSTypeParameter" && followingNode.name) { addLeadingComment(followingNode.name, comment); return true; } if (precedingNode && precedingNode.type === "TSTypeParameter" && precedingNode.constraint) { addTrailingComment(precedingNode.constraint, comment); return true; } return false; } export function isBlockComment(comment) { return comment.type === "Block" || comment.type === "CommentBlock"; } //# sourceMappingURL=comments.js.map