diff --git a/src/comments/handler.js b/src/comments/handler.js index 05d85921f..b3edfbe23 100644 --- a/src/comments/handler.js +++ b/src/comments/handler.js @@ -6,6 +6,7 @@ const { } = require('../prettier-comments/language-js/comments'); const handleContractDefinitionComments = require('./handlers/ContractDefinition'); +const handleMemberAccessComments = require('./handlers/MemberAccess'); function solidityHandleOwnLineComment( comment, @@ -26,6 +27,7 @@ function solidityHandleOwnLineComment( if ( handleContractDefinitionComments(...handlerArguments) || + handleMemberAccessComments(...handlerArguments) || handleOwnLineComment(comment, text, options, ast, isLastComment) ) { return true; @@ -52,6 +54,7 @@ function solidityHandleEndOfLineComment( if ( handleContractDefinitionComments(...handlerArguments) || + handleMemberAccessComments(...handlerArguments) || handleEndOfLineComment(comment, text, options, ast, isLastComment) ) { return true; @@ -78,6 +81,7 @@ function solidityHandleRemainingComment( if ( handleContractDefinitionComments(...handlerArguments) || + handleMemberAccessComments(...handlerArguments) || handleRemainingComment(comment, text, options, ast, isLastComment) ) { return true; diff --git a/src/comments/handlers/MemberAccess.js b/src/comments/handlers/MemberAccess.js new file mode 100644 index 000000000..711741832 --- /dev/null +++ b/src/comments/handlers/MemberAccess.js @@ -0,0 +1,19 @@ +const { + util: { addDanglingComment } +} = require('prettier'); + +function handleMemberAccessComments( + text, + precedingNode, + enclosingNode, + followingNode, + comment +) { + if (enclosingNode && enclosingNode.type === 'MemberAccess') { + addDanglingComment(enclosingNode, comment); + return true; + } + return false; +} + +module.exports = handleMemberAccessComments; diff --git a/src/nodes/MemberAccess.js b/src/nodes/MemberAccess.js index 698e54ffc..1e1834ca0 100644 --- a/src/nodes/MemberAccess.js +++ b/src/nodes/MemberAccess.js @@ -1,8 +1,9 @@ const { doc: { - builders: { group, indent, label, softline } + builders: { group, hardline, indent, label, softline } } } = require('prettier'); +const printComments = require('./print-comments'); const isEndOfChain = (node, path) => { let i = 0; @@ -94,33 +95,68 @@ const isEndOfChain = (node, path) => { * be printed. */ const processChain = (chain) => { - const firstSeparatorIndex = chain.findIndex( + // Extract comments and reverse the order of print. + const comments = chain + .filter((element) => element.label && element.label === 'comments') + .reverse(); + const chainContent = chain.filter( + (element) => !element.label || element.label !== 'comments' + ); + + const firstSeparatorIndex = chainContent.findIndex( (element) => element.label === 'separator' ); // The doc[] before the first separator - const firstExpression = chain.slice(0, firstSeparatorIndex); + const firstExpression = chainContent.slice(0, firstSeparatorIndex); // The doc[] containing the rest of the chain - const restOfChain = group(indent(chain.slice(firstSeparatorIndex))); + const restOfChain = group(indent(chainContent.slice(firstSeparatorIndex))); // We wrap the expression in a label in case there is an IndexAccess or // a FunctionCall following this MemberAccess. - return label('MemberAccessChain', group([firstExpression, restOfChain])); + return label('MemberAccessChain', [ + comments, + group([firstExpression, restOfChain]) + ]); }; const MemberAccess = { - print: ({ node, path, print }) => { + print: ({ node, path, print, options }) => { + let comments; + const extractComments = (expressionPath) => { + const expression = expressionPath.getValue(); + if (expression.type === 'FunctionCall') { + expressionPath.call(extractComments, 'expression'); + return; + } + if (expression.type === 'IndexAccess') { + expressionPath.call(extractComments, 'base'); + return; + } + comments = printComments(expression, path, options); + if (comments) { + comments = label('comments', [comments, hardline]); + } + }; + + path.call(extractComments, 'expression'); + let expressionDoc = path.call(print, 'expression'); - if (Array.isArray(expressionDoc)) { - expressionDoc = expressionDoc.flat(); - } + expressionDoc = Array.isArray(expressionDoc) + ? expressionDoc.flat() + : [expressionDoc]; const doc = [ - expressionDoc, + comments, + ...expressionDoc, label('separator', [softline, '.']), node.memberName ].flat(); - return isEndOfChain(node, path) ? processChain(doc) : doc; + if (isEndOfChain(node, path)) { + extractComments(path); + return processChain([comments, doc].flat()); + } + return doc; } }; diff --git a/tests/config/format-test.js b/tests/config/format-test.js index 4b1f52fb4..afa82a68f 100644 --- a/tests/config/format-test.js +++ b/tests/config/format-test.js @@ -22,7 +22,7 @@ const RANGE_END_PLACEHOLDER = "<<>>"; // Here we add files that will not be the same when formatting a second time. const unstableTests = new Map( - ["Comments/Comments.sol"].map((fixture) => { + ["Comments/Comments.sol", "MemberAccess/MemberAccess.sol"].map((fixture) => { const [file, isUnstable = () => true] = Array.isArray(fixture) ? fixture : [fixture]; @@ -32,7 +32,7 @@ const unstableTests = new Map( // Here we add files that will not have the same AST after being formatted. const unstableAstTests = new Map( - [].map((fixture) => { + ["MemberAccess/MemberAccess.sol"].map((fixture) => { const [file, isAstUnstable = () => true] = Array.isArray(fixture) ? fixture : [fixture]; diff --git a/tests/format/MemberAccess/MemberAccess.sol b/tests/format/MemberAccess/MemberAccess.sol index 04f6aaee0..442054ca1 100644 --- a/tests/format/MemberAccess/MemberAccess.sol +++ b/tests/format/MemberAccess/MemberAccess.sol @@ -57,6 +57,59 @@ contract MemberAccess { path, address(this, aoeu, aoeueu, aoeu) ); + + // Comment before chain + game. + // CONFIG + config. + // Comment 1 + // Comment 2 + resolveWindow. + // Comment 3 + functionCall(/* inside function comment */). + // Comment 4 + array[/* inside array comment */ 1]. + // Comment 5 + call{value: 10, gas: 800}(); + + + // Comment before chain + game. + // CONFIG + config. + // Comment 1 + // Comment 2 + resolveWindow. + // Comment 3 + functionCall(/* inside function comment */). + // Comment 4 + array[/* inside array comment */ 1]. + // Comment 5 + endOfChain; + + + // Comment before chain + game. + // CONFIG + config. + // Comment 1 + // Comment 2 + resolveWindow. + // Comment 3 + functionCall(/* inside function comment */). + // Comment 4 + endOfChain; + + + // Comment before chain + game. + // CONFIG + config. + // Comment 1 + // Comment 2 + resolveWindow. + // Comment 3 + endOfChain; } } diff --git a/tests/format/MemberAccess/__snapshots__/jsfmt.spec.js.snap b/tests/format/MemberAccess/__snapshots__/jsfmt.spec.js.snap index 49b6b565e..31bfff648 100644 --- a/tests/format/MemberAccess/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/MemberAccess/__snapshots__/jsfmt.spec.js.snap @@ -65,6 +65,59 @@ contract MemberAccess { path, address(this, aoeu, aoeueu, aoeu) ); + + // Comment before chain + game. + // CONFIG + config. + // Comment 1 + // Comment 2 + resolveWindow. + // Comment 3 + functionCall(/* inside function comment */). + // Comment 4 + array[/* inside array comment */ 1]. + // Comment 5 + call{value: 10, gas: 800}(); + + + // Comment before chain + game. + // CONFIG + config. + // Comment 1 + // Comment 2 + resolveWindow. + // Comment 3 + functionCall(/* inside function comment */). + // Comment 4 + array[/* inside array comment */ 1]. + // Comment 5 + endOfChain; + + + // Comment before chain + game. + // CONFIG + config. + // Comment 1 + // Comment 2 + resolveWindow. + // Comment 3 + functionCall(/* inside function comment */). + // Comment 4 + endOfChain; + + + // Comment before chain + game. + // CONFIG + config. + // Comment 1 + // Comment 2 + resolveWindow. + // Comment 3 + endOfChain; } } @@ -195,6 +248,59 @@ contract MemberAccess { path, address(this, aoeu, aoeueu, aoeu) ); + + // Comment before chain + // CONFIG + // Comment 1 + // Comment 2 + // Comment 3 + // Comment 4 + // Comment 5 + game + .config + .resolveWindow + .functionCall() /* inside function comment */ + .array[ + /* inside array comment */ + 1 + ] + .call{value: 10, gas: 800}(); + + // Comment before chain + // CONFIG + // Comment 1 + // Comment 2 + // Comment 3 + // Comment 4 + // Comment 5 + game + .config + .resolveWindow + .functionCall() /* inside function comment */ + .array[ + /* inside array comment */ + 1 + ] + .endOfChain; + + // Comment before chain + // CONFIG + // Comment 1 + // Comment 2 + // Comment 3 + // Comment 4 + game + .config + .resolveWindow + .functionCall() /* inside function comment */ + .endOfChain; + + // Comment before chain + // CONFIG + // Comment 1 + // Comment 2 + // Comment 3 + game.config.resolveWindow.endOfChain; } }