diff --git a/.eslintrc.yml b/.eslintrc.yml index f6f8f297..99460c8c 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -5,6 +5,8 @@ root: true env: es6: true node: true +parserOptions: + ecmaVersion: 2018 rules: no-fallthrough: off curly: error diff --git a/packages/java-parser/docs/comments.md b/packages/java-parser/docs/comments.md new file mode 100644 index 00000000..e468ada0 --- /dev/null +++ b/packages/java-parser/docs/comments.md @@ -0,0 +1,58 @@ +# Handling comments + +## Objective + +Attach each comment to a CST node or token as either leading or trailing comment. + +### Attach comment to the highest node possible + +A comment could often be attached at several nodes or token. For instance, in the following code snippet: + +```java +// comment +public void t() {} +``` + +`// comment` could be attached to: + +- the `methodDeclaration` node +- the `methodModifier` node +- the `public` token + +We have made the decision to attach the comment to the highest node possible (the most enclosive one). It makes the more sense to us, and it ease handling comments in the prettier-plugin. + +### Trailing comments + +A comment should be considered as a trailing comment in very specific cases: + +- If it is on the same line as the node/token it followed, and not on the same line as the node/token it preceded. For instance, `//comment` is considered as a trailing comment in the following code snippet: + + ```java + int i = 1; // comment + int j = 2; + ``` + +- If it is at the end of the file + +### Consecutives comments + +If there are consecutive comments, each comment will be considered separately. For instance: + +```java +int i = 1; +// comment1 +// comment2 +int j = 2; +``` + +`// comment1` and `// comment2` will be both attached as leading comments to the second `localVariableDeclarationStatement` node. + +When in: + +```java +int i = 1; // comment1 +// comment2 +int j = 2; +``` + +`// comment1` will be attached as a trailing comment to the first `localVariableDeclarationStatement` node and `// comment2` will be attached as a leading comment to the second one. diff --git a/packages/java-parser/src/comments.js b/packages/java-parser/src/comments.js index 19d1c250..c12134e7 100644 --- a/packages/java-parser/src/comments.js +++ b/packages/java-parser/src/comments.js @@ -1,89 +1,4 @@ "use strict"; -const _ = require("lodash"); - -function attachComments(tokens, comments) { - const attachComments = [...comments]; - - // edge case: when the file contains only one token (;/if no token then EOF) - if (tokens.length === 1) { - attachComments.forEach(comment => { - if (comment.endOffset < tokens[0].startOffset) { - if (!tokens[0].leadingComments) { - tokens[0].leadingComments = []; - } - tokens[0].leadingComments.push(comment); - } else { - if (!tokens[0].trailingComments) { - tokens[0].trailingComments = []; - } - tokens[0].trailingComments.push(comment); - } - }); - return tokens; - } - - // edge case: when the file start with comments, it attaches as leadingComments to the first token - const firstToken = tokens[0]; - const headComments = []; - while ( - attachComments.length > 0 && - attachComments[0].endOffset < firstToken.startOffset - ) { - headComments.push(attachComments[0]); - attachComments.splice(0, 1); - } - - if (headComments.length > 0) { - firstToken.leadingComments = headComments; - } - - // edge case: when the file end with comments, it attaches as trailingComments to the last token - const lastToken = tokens[tokens.length - 1]; - const tailComments = []; - while ( - attachComments.length > 0 && - attachComments[attachComments.length - 1].startOffset > lastToken.endOffset - ) { - tailComments.push(attachComments[attachComments.length - 1]); - attachComments.splice(attachComments.length - 1, 1); - } - - if (tailComments.length > 0) { - lastToken.trailingComments = tailComments.reverse(); - } - - let currentToken = 0; - attachComments.forEach(element => { - // find the correct position to place the comment - while ( - !( - element.startOffset > tokens[currentToken].endOffset && - element.endOffset < tokens[currentToken + 1].startOffset - ) - ) { - currentToken++; - } - - // attach comment to the next token by default, - // it attaches to the current one when the comment and token is on the same line - if ( - element.startLine === tokens[currentToken].endLine && - element.startLine !== tokens[currentToken + 1].startLine - ) { - if (!tokens[currentToken].trailingComments) { - tokens[currentToken].trailingComments = []; - } - tokens[currentToken].trailingComments.push(element); - } else { - if (!tokens[currentToken + 1].leadingComments) { - tokens[currentToken + 1].leadingComments = []; - } - tokens[currentToken + 1].leadingComments.push(element); - } - }); - - return tokens; -} /** * Search where is the position of the comment in the token array by @@ -113,63 +28,211 @@ function findUpperBoundToken(tokens, comment) { return i; } +function isPrettierIgnoreComment(comment) { + return comment.image.match( + /(\/\/(\s*)prettier-ignore(\s*))|(\/\*(\s*)prettier-ignore(\s*)\*\/)/gm + ); +} + /** - * Extends each comments offsets to the left and the right in order to match the - * previous and next token offset. This allow to directly match the prettier-ignore - * comment to the correct CSTNode. - * @param {*} tokens ordered array of tokens - * @param {*} comments array of prettier-ignore comments - * @return prettier-ignore comment array with extended location + * Pre-processing of tokens in order to + * complete the parser's mostEnclosiveCstNodeByStartOffset and mostEnclosiveCstNodeByEndOffset structures. + * + * @param {ITokens[]} tokens - array of tokens + * @param {{[startOffset: number]: CSTNode}} mostEnclosiveCstNodeByStartOffset + * @param {{[endOffset: number]: CSTNode}} mostEnclosiveCstNodeByEndOffset + */ +function completeMostEnclosiveCSTNodeByOffset( + tokens, + mostEnclosiveCstNodeByStartOffset, + mostEnclosiveCstNodeByEndOffset +) { + tokens.forEach(token => { + if (mostEnclosiveCstNodeByStartOffset[token.startOffset] === undefined) { + mostEnclosiveCstNodeByStartOffset[token.startOffset] = token; + } + + if (mostEnclosiveCstNodeByEndOffset[token.endOffset] === undefined) { + mostEnclosiveCstNodeByEndOffset[token.endOffset] = token; + } + }); +} + +/** + * Create two data structures we use to know at which offset a comment can be attached. + * - commentsByExtendedStartOffset: map a comment by the endOffset of the previous token. + * - commentsByExtendedEndOffset: map a comment by the startOffset of the next token + * + * @param {ITokens[]} tokens - array of tokens + * @param {[]} comments - array of comments + * + * @return {{commentsByExtendedStartOffset: {[extendedStartOffset: number]: Comment[]}, commentsByExtendedEndOffset: {[extendedEndOffset: number]: Comment[]}}} */ -function extendCommentRange(tokens, comments) { - const ignoreComments = [...comments]; +function mapCommentsByExtendedRange(tokens, comments) { + const commentsByExtendedEndOffset = {}; + const commentsByExtendedStartOffset = {}; + let position; - ignoreComments.forEach(comment => { + comments.forEach(comment => { position = findUpperBoundToken(tokens, comment); - comment.extendedRange = {}; - comment.extendedRange.startOffset = + + const extendedStartOffset = position - 1 < 0 ? comment.startOffset : tokens[position - 1].endOffset; - comment.extendedRange.endOffset = + const extendedEndOffset = position == tokens.length ? comment.endOffset : tokens[position].startOffset; + comment.extendedOffset = { + endOffset: extendedEndOffset + }; + + if (commentsByExtendedEndOffset[extendedEndOffset] === undefined) { + commentsByExtendedEndOffset[extendedEndOffset] = [comment]; + } else { + commentsByExtendedEndOffset[extendedEndOffset].push(comment); + } + + if (commentsByExtendedStartOffset[extendedStartOffset] === undefined) { + commentsByExtendedStartOffset[extendedStartOffset] = [comment]; + } else { + commentsByExtendedStartOffset[extendedStartOffset].push(comment); + } }); - return ignoreComments; -} -function filterPrettierIgnore(comments) { - return [...comments].filter(comment => - comment.image.match( - /(\/\/(\s*)prettier-ignore(\s*))|(\/\*(\s*)prettier-ignore(\s*)\*\/)/gm - ) - ); + return { commentsByExtendedEndOffset, commentsByExtendedStartOffset }; } -function shouldIgnore(node, comments, ignoredNodes) { - const matchingComment = _.find( - comments, - comment => comment.extendedRange.endOffset === node.location.startOffset - ); - if (matchingComment) { - ignoredNodes[matchingComment.startOffset] = node; +/** + * Determine if a comment should be attached as a trailing comment to a specific node. + * A comment should be trailing if it is on the same line than the previous token and + * not on the same line than the next token + * + * @param {*} comment + * @param {CSTNode} node + * @param {{[startOffset: number]: CSTNode}} mostEnclosiveCstNodeByStartOffset + */ +function shouldAttachTrailingComments( + comment, + node, + mostEnclosiveCstNodeByStartOffset +) { + if (isPrettierIgnoreComment(comment)) { + return false; } + + const nextNode = + mostEnclosiveCstNodeByStartOffset[comment.extendedOffset.endOffset]; + + // Last node of the file + if (nextNode === undefined) { + return true; + } + + const nodeEndLine = + node.location !== undefined ? node.location.endLine : node.endLine; + + if (comment.startLine !== nodeEndLine) { + return false; + } + + const nextNodeStartLine = + nextNode.location !== undefined + ? nextNode.location.startLine + : nextNode.startLine; + return comment.endLine !== nextNodeStartLine; } -function attachIgnoreNodes(ignoreComments, ignoredNodes) { - ignoreComments.forEach(comment => { - if (ignoredNodes[comment.startOffset]) { - ignoredNodes[comment.startOffset].ignore = true; +/** + * Attach comments to the most enclosive CSTNode (node or token) + * + * @param {ITokens[]} tokens + * @param {*} comments + * @param {{[startOffset: number]: CSTNode}} mostEnclosiveCstNodeByStartOffset + * @param {{[endOffset: number]: CSTNode}} mostEnclosiveCstNodeByEndOffset + */ +function attachComments( + tokens, + comments, + mostEnclosiveCstNodeByStartOffset, + mostEnclosiveCstNodeByEndOffset +) { + // Edge case: only comments in the file + if (tokens.length === 0) { + mostEnclosiveCstNodeByStartOffset[NaN].leadingComments = comments; + return; + } + + // Pre-processing phase to complete the data structures we need to attach + // a comment to the right place + completeMostEnclosiveCSTNodeByOffset( + tokens, + mostEnclosiveCstNodeByStartOffset, + mostEnclosiveCstNodeByEndOffset + ); + const { + commentsByExtendedStartOffset, + commentsByExtendedEndOffset + } = mapCommentsByExtendedRange(tokens, comments); + + /* + This set is here to ensure that we attach comments only once + If a comment is attached to a node or token, we remove it from this set + */ + const commentsToAttach = new Set(comments); + + // Attach comments as trailing comments if desirable + Object.keys(mostEnclosiveCstNodeByEndOffset).forEach(endOffset => { + // We look if some comments is directly following this node/token + if (commentsByExtendedStartOffset[endOffset] !== undefined) { + const nodeTrailingComments = commentsByExtendedStartOffset[ + endOffset + ].filter(comment => { + return ( + shouldAttachTrailingComments( + comment, + mostEnclosiveCstNodeByEndOffset[endOffset], + mostEnclosiveCstNodeByStartOffset + ) && commentsToAttach.has(comment) + ); + }); + + if (nodeTrailingComments.length > 0) { + mostEnclosiveCstNodeByEndOffset[ + endOffset + ].trailingComments = nodeTrailingComments; + } + + nodeTrailingComments.forEach(comment => { + commentsToAttach.delete(comment); + }); } }); -} -function ignoredComments(tokens, comments) { - return extendCommentRange(tokens, filterPrettierIgnore(comments)); + // Attach rest of comments as leading comments + Object.keys(mostEnclosiveCstNodeByStartOffset).forEach(startOffset => { + // We look if some comments is directly preceding this node/token + if (commentsByExtendedEndOffset[startOffset] !== undefined) { + const nodeLeadingComments = commentsByExtendedEndOffset[ + startOffset + ].filter(comment => commentsToAttach.has(comment)); + + if (nodeLeadingComments.length > 0) { + mostEnclosiveCstNodeByStartOffset[ + startOffset + ].leadingComments = nodeLeadingComments; + } + + // prettier ignore support + for (let i = 0; i < nodeLeadingComments.length; i++) { + if (isPrettierIgnoreComment(nodeLeadingComments[i])) { + mostEnclosiveCstNodeByStartOffset[startOffset].ignore = true; + break; + } + } + } + }); } module.exports = { - attachComments, - shouldIgnore, - ignoredComments, - attachIgnoreNodes + attachComments }; diff --git a/packages/java-parser/src/index.js b/packages/java-parser/src/index.js index c1e48a13..81e018b1 100644 --- a/packages/java-parser/src/index.js +++ b/packages/java-parser/src/index.js @@ -1,11 +1,7 @@ "use strict"; const JavaLexer = require("./lexer"); const JavaParser = require("./parser"); -const { - attachComments, - ignoredComments, - attachIgnoreNodes -} = require("./comments"); +const { attachComments } = require("./comments"); const parser = new JavaParser(); @@ -29,13 +25,8 @@ function parse(inputText, entryPoint = "compilationUnit") { } parser.input = lexResult.tokens; - - // prettier-ignore support - const ignoreComments = ignoredComments( - lexResult.tokens, - lexResult.groups.comments - ); - parser.setIgnoredComments(ignoreComments); + parser.mostEnclosiveCstNodeByStartOffset = {}; + parser.mostEnclosiveCstNodeByEndOffset = {}; // Automatic CST created when parsing const cst = parser[entryPoint](); @@ -54,19 +45,12 @@ function parse(inputText, entryPoint = "compilationUnit") { ); } - // only comments code support - // https://github.com/jhipster/prettier-java/pull/217 - if (lexResult.tokens.length === 0) { - const EOF = Object.assign({}, cst.children.EOF[0]); - EOF.startOffset = Number.MAX_SAFE_INTEGER; - EOF.endOffset = Number.MAX_SAFE_INTEGER; - cst.children.EOF = [EOF]; - attachComments(cst.children.EOF, lexResult.groups.comments); - } else { - attachComments(lexResult.tokens, lexResult.groups.comments); - } - - attachIgnoreNodes(ignoreComments, parser.ignoredNodes); + attachComments( + lexResult.tokens, + lexResult.groups.comments, + parser.mostEnclosiveCstNodeByStartOffset, + parser.mostEnclosiveCstNodeByEndOffset + ); return cst; } diff --git a/packages/java-parser/src/parser.js b/packages/java-parser/src/parser.js index fc34f9ab..62f23403 100644 --- a/packages/java-parser/src/parser.js +++ b/packages/java-parser/src/parser.js @@ -10,7 +10,6 @@ const interfaces = require("./productions/interfaces"); const arrays = require("./productions/arrays"); const blocksStatements = require("./productions/blocks-and-statements"); const expressions = require("./productions/expressions"); -const { shouldIgnore } = require("./comments"); const { getSkipValidations } = require("./utils"); /** @@ -48,8 +47,9 @@ class JavaParser extends Parser { }); const $ = this; - this.ignoreNodes = {}; - this.ignoreComments = []; + + this.mostEnclosiveCstNodeByStartOffset = {}; + this.mostEnclosiveCstNodeByEndOffset = {}; // --------------------- // Productions from ยง3 (Lexical Structure) @@ -81,7 +81,12 @@ class JavaParser extends Parser { cstPostNonTerminal(ruleCstResult, ruleName) { super.cstPostNonTerminal(ruleCstResult, ruleName); if (this.isBackTracking() === false) { - shouldIgnore(ruleCstResult, this.ignoredComments, this.ignoredNodes); + this.mostEnclosiveCstNodeByStartOffset[ + ruleCstResult.location.startOffset + ] = ruleCstResult; + this.mostEnclosiveCstNodeByEndOffset[ + ruleCstResult.location.endOffset + ] = ruleCstResult; } } @@ -106,11 +111,6 @@ class JavaParser extends Parser { } }); } - - setIgnoredComments(comments) { - this.ignoredNodes = {}; - this.ignoredComments = [...comments]; - } } module.exports = JavaParser; diff --git a/packages/prettier-plugin-java/src/cst-printer.js b/packages/prettier-plugin-java/src/cst-printer.js index d061622b..95f9fefa 100644 --- a/packages/prettier-plugin-java/src/cst-printer.js +++ b/packages/prettier-plugin-java/src/cst-printer.js @@ -18,10 +18,7 @@ const { const { PackagesAndModulesPrettierVisitor } = require("./printers/packages-and-modules"); -const { - buildOriginalText, - getCSTNodeStartEndToken -} = require("./printers/printer-utils"); +const { processCommentsOnNode } = require("./printers/comments"); class CstPrettierPrinter extends BaseJavaCstVisitor { constructor() { @@ -77,12 +74,16 @@ class CstPrettierPrinter extends BaseJavaCstVisitor { if (ctx.ignore) { try { - const startEndTokens = getCSTNodeStartEndToken(ctx); - return buildOriginalText( - startEndTokens[0], - startEndTokens[1], - this.originalText - ); + const startOffset = + ctx.leadingComments !== undefined + ? ctx.leadingComments[0].startOffset + : ctx.location.startOffset; + const endOffset = + ctx.trailingComments !== undefined + ? ctx.trailingComments[ctx.trailingComments.length - 1].endOffset + : ctx.location.endOffset; + + return this.originalText.substring(startOffset, endOffset + 1); } catch (e) { throw Error( e + @@ -91,7 +92,7 @@ class CstPrettierPrinter extends BaseJavaCstVisitor { } } - return orgVisit.call(this, ctx, inParam); + return processCommentsOnNode(ctx, orgVisit.call(this, ctx, inParam)); }; } } diff --git a/packages/prettier-plugin-java/src/printers/blocks-and-statements.js b/packages/prettier-plugin-java/src/printers/blocks-and-statements.js index 13d8f0bc..55649cdf 100644 --- a/packages/prettier-plugin-java/src/printers/blocks-and-statements.js +++ b/packages/prettier-plugin-java/src/printers/blocks-and-statements.js @@ -2,13 +2,8 @@ /* eslint-disable no-unused-vars */ const { line, softline, hardline } = require("prettier").doc.builders; -const { - group, - indent, - concat, - join, - getImageWithComments -} = require("./prettier-builder"); +const { group, indent, concat, join } = require("./prettier-builder"); +const { printTokenWithComments } = require("./comments"); const { displaySemicolon, rejectAndConcat, @@ -19,7 +14,9 @@ const { putIntoBraces, putIntoCurlyBraces, isStatementEmptyStatement, - sortModifiers + sortModifiers, + hasTrailingLineComments, + hasLeadingLineComments } = require("./printer-utils"); class BlocksAndStatementPrettierVisitor { @@ -76,10 +73,35 @@ class BlocksAndStatementPrettierVisitor { return this.visitSingle(ctx); } - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } statement(ctx, params) { + // handling Labeled statements comments + if (ctx.labeledStatement !== undefined) { + const newLabelStatement = { ...ctx.labeledStatement[0] }; + const newColon = { ...ctx.labeledStatement[0].children.Colon[0] }; + const newStatement = { ...ctx.labeledStatement[0].children.statement[0] }; + + const labeledStatementLeadingComments = []; + + if (newColon.trailingComments !== undefined) { + labeledStatementLeadingComments.push(...newColon.trailingComments); + delete newColon.trailingComments; + } + + if (newStatement.leadingComments !== undefined) { + labeledStatementLeadingComments.push(...newStatement.leadingComments); + delete newStatement.leadingComments; + } + + newLabelStatement.leadingComments = labeledStatementLeadingComments; + newLabelStatement.children.Colon[0] = newColon; + newLabelStatement.children.statement[0] = newStatement; + + return this.visit([newLabelStatement]); + } + return this.visitSingle(ctx, params); } @@ -122,8 +144,14 @@ class BlocksAndStatementPrettierVisitor { }); const elseSeparator = isStatementEmptyStatement(elseStatement) ? "" : " "; + const elseOnSameLine = + hasTrailingLineComments(ctx.statement[0]) || + hasLeadingLineComments(ctx.Else[0]) + ? hardline + : " "; + elsePart = rejectAndJoin(elseSeparator, [ - concat([" ", ctx.Else[0]]), + concat([elseOnSameLine, ctx.Else[0]]), elseStatement ]); } @@ -245,11 +273,20 @@ class BlocksAndStatementPrettierVisitor { const statementSeparator = isStatementEmptyStatement(statement) ? "" : " "; return rejectAndConcat([ - rejectAndJoin(" ", [ctx.For[0], ctx.LBrace[0]]), - forInit, - rejectAndJoin(" ", [ctx.Semicolon[0], expression]), - rejectAndJoin(" ", [ctx.Semicolon[1], forUpdate]), - concat([ctx.RBrace[0], statementSeparator]), + rejectAndJoin(" ", [ + ctx.For[0], + putIntoBraces( + rejectAndConcat([ + forInit, + rejectAndJoin(line, [ctx.Semicolon[0], expression]), + rejectAndJoin(line, [ctx.Semicolon[1], forUpdate]) + ]), + softline, + ctx.LBrace[0], + ctx.RBrace[0] + ) + ]), + statementSeparator, statement ]); } @@ -266,7 +303,7 @@ class BlocksAndStatementPrettierVisitor { const statementExpressions = this.mapVisit(ctx.statementExpression); const commas = ctx.Comma ? ctx.Comma.map(elt => { - return concat([getImageWithComments(elt), " "]); + return concat([printTokenWithComments(elt), " "]); }) : []; return rejectAndJoinSeps(commas, statementExpressions); diff --git a/packages/prettier-plugin-java/src/printers/classes.js b/packages/prettier-plugin-java/src/printers/classes.js index 3f20260c..337065be 100644 --- a/packages/prettier-plugin-java/src/printers/classes.js +++ b/packages/prettier-plugin-java/src/printers/classes.js @@ -12,17 +12,15 @@ const { displaySemicolon, putIntoBraces, putIntoCurlyBraces, - getClassBodyDeclarationsSeparator + getClassBodyDeclarationsSeparator, + isStatementEmptyStatement } = require("./printer-utils"); +const { concat, join, group, indent } = require("./prettier-builder"); const { - concat, - join, - group, - indent, - getImageWithComments, - getLeadingComments, - getTrailingComments -} = require("./prettier-builder"); + printTokenWithComments, + getTokenLeadingComments, + getTokenTrailingComments +} = require("./comments"); class ClassesPrettierVisitor { classDeclaration(ctx) { @@ -78,7 +76,7 @@ class ClassesPrettierVisitor { return this.visit(ctx.annotation); } // public | protected | private | ... - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } typeParameters(ctx) { @@ -181,7 +179,7 @@ class ClassesPrettierVisitor { return this.visit(ctx.annotation); } // public | protected | private | ... - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } variableDeclaratorList(ctx) { @@ -289,7 +287,7 @@ class ClassesPrettierVisitor { if (ctx.numericType) { return this.visitSingle(ctx); } - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } unannReferenceType(ctx) { @@ -343,7 +341,7 @@ class ClassesPrettierVisitor { } unannTypeVariable(ctx) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } methodDeclaration(ctx) { @@ -353,13 +351,8 @@ class ClassesPrettierVisitor { const header = this.visit(ctx.methodHeader); const body = this.visit(ctx.methodBody); - const headerBodySeparator = - body !== undefined && - body.type === "concat" && - body.parts && - body.parts.includes(";") - ? "" - : " "; + + const headerBodySeparator = isStatementEmptyStatement(body) ? "" : " "; return rejectAndJoin(hardline, [ rejectAndJoin(hardline, firstAnnotations), @@ -375,7 +368,7 @@ class ClassesPrettierVisitor { return this.visit(ctx.annotation); } // public | protected | private | Synchronized | ... - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } methodHeader(ctx) { @@ -408,11 +401,11 @@ class ClassesPrettierVisitor { return this.visit(ctx.unannType); } // void - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } methodDeclarator(ctx) { - const identifier = getImageWithComments(ctx.Identifier[0]); + const identifier = printTokenWithComments(ctx.Identifier[0]); const formalParameterList = this.visit(ctx.formalParameterList); const dims = this.visit(ctx.dims); @@ -484,7 +477,7 @@ class ClassesPrettierVisitor { if (ctx.annotation) { return this.visit(ctx.annotation); } - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } throws(ctx) { @@ -507,7 +500,7 @@ class ClassesPrettierVisitor { return this.visit(ctx.block); } - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } instanceInitializer(ctx) { @@ -554,7 +547,7 @@ class ClassesPrettierVisitor { return this.visit(ctx.annotation); } // public | protected | private | Synchronized | ... - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } constructorDeclarator(ctx) { @@ -576,7 +569,7 @@ class ClassesPrettierVisitor { } simpleTypeName(ctx) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } constructorBody(ctx) { @@ -604,7 +597,6 @@ class ClassesPrettierVisitor { const argumentList = this.visit(ctx.argumentList); return rejectAndConcat([ typeArguments, - " ", keyWord, group( rejectAndConcat([ @@ -717,9 +709,9 @@ class ClassesPrettierVisitor { } return concat([ - getLeadingComments(ctx.Semicolon[0]), + ...getTokenLeadingComments(ctx.Semicolon[0]), "", - getTrailingComments(ctx.Semicolon[0]) + ...getTokenTrailingComments(ctx.Semicolon[0]) ]); } diff --git a/packages/prettier-plugin-java/src/printers/comments.js b/packages/prettier-plugin-java/src/printers/comments.js new file mode 100644 index 00000000..fd763c6d --- /dev/null +++ b/packages/prettier-plugin-java/src/printers/comments.js @@ -0,0 +1,229 @@ +"use strict"; +const { + concat, + hardline, + lineSuffix, + breakParent, + literalline +} = require("prettier").doc.builders; + +/** + * Takes a token and return a doc with: + * - concatenated leading comments + * - the token image + * - concatenated trailing comments + * + * @param {Token} token + * @return a doc with the token and its comments + */ +function printTokenWithComments(token) { + return printWithComments( + token, + token.image, + getTokenLeadingComments, + getTokenTrailingComments + ); +} + +/** + * Takes a node and return a doc with: + * - concatenated leading comments + * - the node doc value + * - concatenated trailing comments + * + * @param {CSTNode} node + * @param {Doc} value - the converted node value + * @return a doc with the token and its comments + */ +function printNodeWithComments(node, value) { + return printWithComments( + node, + value, + getNodeLeadingComments, + getNodeTrailingComments + ); +} + +function printWithComments( + nodeOrToken, + value, + getLeadingComments, + getTrailingComments +) { + const leadingComments = getLeadingComments(nodeOrToken); + const trailingComments = getTrailingComments(nodeOrToken, value); + + return leadingComments.length === 0 && trailingComments.length === 0 + ? value + : concat([...leadingComments, value, ...trailingComments]); +} + +/** + * @param {Token} token + * @return an array containing processed leading comments and separators + */ +function getTokenLeadingComments(token) { + return getLeadingComments(token, token); +} + +/** + * @param {CSTNode} node + * @return an array containing processed leading comments and separators + */ +function getNodeLeadingComments(node) { + return getLeadingComments(node, node.location); +} + +function getLeadingComments(nodeOrToken, location) { + const arr = []; + if (Object.prototype.hasOwnProperty.call(nodeOrToken, "leadingComments")) { + nodeOrToken.leadingComments.forEach(comment => { + if ( + comment.tokenType.name === "LineComment" || + comment.endLine !== location.startLine || + comment.startOffset > location.startOffset + ) { + arr.push(concat(formatComment(comment))); + arr.push(hardline); + } else { + arr.push(concat(formatComment(comment))); + } + }); + } + + return arr; +} + +/** + * @param {Token} token + * @return an array containing processed trailing comments and separators + */ +function getTokenTrailingComments(token) { + return getTrailingComments(token, token.image, token); +} + +/** + * @param {CSTNode} node + * @return an array containing processed trailing comments and separators + */ +function getNodeTrailingComments(node, value) { + return getTrailingComments(node, value, node.location); +} + +function getTrailingComments(nodeOrToken, value, location) { + const arr = []; + let previousEndLine = location.endLine; + if (Object.prototype.hasOwnProperty.call(nodeOrToken, "trailingComments")) { + nodeOrToken.trailingComments.forEach((comment, idx) => { + let separator = ""; + + if (comment.startLine !== previousEndLine) { + arr.push(hardline); + } else if (value !== "" && idx === 0) { + separator = " "; + } + + if (comment.tokenType.name === "LineComment") { + arr.push( + lineSuffix( + concat([separator, concat(formatComment(comment)), breakParent]) + ) + ); + } else { + arr.push(concat(formatComment(comment))); + } + + previousEndLine = comment.endLine; + }); + } + + return arr; +} + +function isJavaDoc(comment, lines) { + let isJavaDoc = true; + if (comment.tokenType.name === "TraditionalComment" && lines.length > 1) { + for (let i = 1; i < lines.length; i++) { + if (lines[i].trim().charAt(0) !== "*") { + isJavaDoc = false; + break; + } + } + } else { + isJavaDoc = false; + } + + return isJavaDoc; +} + +function formatJavaDoc(lines) { + const res = [lines[0].trim()]; + + for (let i = 1; i < lines.length; i++) { + res.push(hardline); + res.push(" " + lines[i].trim()); + } + + return res; +} + +function formatComment(comment) { + const res = []; + const lines = comment.image.split("\n"); + + if (isJavaDoc(comment, lines)) { + return formatJavaDoc(lines); + } + + lines.forEach(line => { + res.push(line); + res.push(literalline); + }); + res.pop(); + return res; +} + +function isToken(doc) { + return ( + doc && Object.prototype.hasOwnProperty.call(doc, "image") && doc.tokenType + ); +} + +function processComments(docs) { + if (!Array.isArray(docs)) { + if (isToken(docs)) { + return printTokenWithComments(docs); + } + return docs; + } + return docs.map(elt => { + if (isToken(elt)) { + return printTokenWithComments(elt); + } + return elt; + }); +} + +function processCommentsOnNode(ctx, value) { + if (!Array.isArray(ctx)) { + return printNodeWithComments(ctx, value); + } + + if (ctx.length === 1) { + return printNodeWithComments(ctx[0], value); + } + + return concat( + ctx.map(elt => { + return printNodeWithComments(elt, value); + }) + ); +} + +module.exports = { + processComments, + printTokenWithComments, + getTokenLeadingComments, + getTokenTrailingComments, + processCommentsOnNode +}; diff --git a/packages/prettier-plugin-java/src/printers/expressions.js b/packages/prettier-plugin-java/src/printers/expressions.js index 3efebe80..30af8f93 100644 --- a/packages/prettier-plugin-java/src/printers/expressions.js +++ b/packages/prettier-plugin-java/src/printers/expressions.js @@ -3,12 +3,8 @@ const _ = require("lodash"); const { ifBreak, line, softline, hardline } = require("prettier").doc.builders; -const { - concat, - group, - indent, - getImageWithComments -} = require("./prettier-builder"); +const { concat, group, indent } = require("./prettier-builder"); +const { printTokenWithComments } = require("./comments"); const { matchCategory, rejectAndJoin, @@ -44,7 +40,7 @@ class ExpressionsPrettierVisitor { return this.visitSingle(ctx); } - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } lambdaParametersWithBraces(ctx) { @@ -122,7 +118,7 @@ class ExpressionsPrettierVisitor { if (ctx.unannType) { return this.visitSingle(ctx); } - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } lambdaBody(ctx) { @@ -330,7 +326,7 @@ class ExpressionsPrettierVisitor { primaryPrefix(ctx, params) { if (ctx.This || ctx.Void || ctx.Boolean) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } return this.visitSingle(ctx, params); diff --git a/packages/prettier-plugin-java/src/printers/interfaces.js b/packages/prettier-plugin-java/src/printers/interfaces.js index 06363343..5c45d195 100644 --- a/packages/prettier-plugin-java/src/printers/interfaces.js +++ b/packages/prettier-plugin-java/src/printers/interfaces.js @@ -2,12 +2,8 @@ /* eslint-disable no-unused-vars */ const { line, softline, hardline } = require("prettier").doc.builders; -const { - concat, - group, - indent, - getImageWithComments -} = require("./prettier-builder"); +const { concat, group, indent } = require("./prettier-builder"); +const { printTokenWithComments } = require("./comments"); const { rejectAndConcat, rejectAndJoin, @@ -16,7 +12,8 @@ const { getInterfaceBodyDeclarationsSeparator, putIntoBraces, putIntoCurlyBraces, - displaySemicolon + displaySemicolon, + isStatementEmptyStatement } = require("./printer-utils"); class InterfacesPrettierVisitor { @@ -64,7 +61,7 @@ class InterfacesPrettierVisitor { if (ctx.annotation) { return this.visitSingle(ctx); } - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } extendsInterfaces(ctx) { @@ -132,7 +129,7 @@ class InterfacesPrettierVisitor { if (ctx.annotation) { return this.visitSingle(ctx); } - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } interfaceMethodDeclaration(ctx) { @@ -142,13 +139,7 @@ class InterfacesPrettierVisitor { const methodHeader = this.visit(ctx.methodHeader); const methodBody = this.visit(ctx.methodBody); - const separator = - methodBody !== undefined && - methodBody.type === "concat" && - methodBody.parts && - methodBody.parts.includes(";") - ? "" - : " "; + const separator = isStatementEmptyStatement(methodBody) ? "" : " "; return rejectAndJoin(hardline, [ rejectAndJoin(hardline, firstAnnotations), @@ -163,7 +154,7 @@ class InterfacesPrettierVisitor { if (ctx.annotation) { return this.visitSingle(ctx); } - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } annotationTypeDeclaration(ctx) { @@ -195,7 +186,7 @@ class InterfacesPrettierVisitor { annotationTypeMemberDeclaration(ctx) { if (ctx.Semicolon) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } return this.visitSingle(ctx); } @@ -232,7 +223,7 @@ class InterfacesPrettierVisitor { if (ctx.annotation) { return this.visitSingle(ctx); } - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } defaultValue(ctx) { diff --git a/packages/prettier-plugin-java/src/printers/lexical-structure.js b/packages/prettier-plugin-java/src/printers/lexical-structure.js index a3180525..5e15a233 100644 --- a/packages/prettier-plugin-java/src/printers/lexical-structure.js +++ b/packages/prettier-plugin-java/src/printers/lexical-structure.js @@ -1,24 +1,24 @@ "use strict"; /* eslint-disable no-unused-vars */ -const { getImageWithComments } = require("./prettier-builder"); +const { printTokenWithComments } = require("./comments"); class LexicalStructurePrettierVisitor { literal(ctx) { if (ctx.CharLiteral || ctx.StringLiteral || ctx.Null) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } return this.visitSingle(ctx); } integerLiteral(ctx) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } floatingPointLiteral(ctx) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } booleanLiteral(ctx) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } } diff --git a/packages/prettier-plugin-java/src/printers/names.js b/packages/prettier-plugin-java/src/printers/names.js index c61b2e2d..b82c96e3 100644 --- a/packages/prettier-plugin-java/src/printers/names.js +++ b/packages/prettier-plugin-java/src/printers/names.js @@ -2,11 +2,11 @@ /* eslint-disable no-unused-vars */ const { buildFqn } = require("./printer-utils"); -const { getImageWithComments } = require("./prettier-builder"); +const { printTokenWithComments } = require("./comments"); class NamesPrettierVisitor { typeIdentifier(ctx) { - return getImageWithComments(ctx.Identifier[0]); + return printTokenWithComments(ctx.Identifier[0]); } moduleName(ctx) { @@ -26,7 +26,7 @@ class NamesPrettierVisitor { } methodName(ctx) { - return getImageWithComments(ctx.Identifier[0]); + return printTokenWithComments(ctx.Identifier[0]); } packageOrTypeName(ctx) { diff --git a/packages/prettier-plugin-java/src/printers/packages-and-modules.js b/packages/prettier-plugin-java/src/printers/packages-and-modules.js index 52bb1b8b..dbaa2b82 100644 --- a/packages/prettier-plugin-java/src/printers/packages-and-modules.js +++ b/packages/prettier-plugin-java/src/printers/packages-and-modules.js @@ -2,7 +2,8 @@ /* eslint-disable no-unused-vars */ const { line, hardline, indent, group } = require("prettier").doc.builders; -const { concat, join, getImageWithComments } = require("./prettier-builder"); +const { concat, join } = require("./prettier-builder"); +const { printTokenWithComments } = require("./comments"); const { buildFqn, rejectAndJoin, @@ -18,7 +19,13 @@ class PackagesAndModulesPrettierVisitor { compilationUnit(ctx) { const compilationUnit = ctx.ordinaryCompilationUnit || ctx.modularCompilationUnit; - return concat([this.visit(compilationUnit[0]), ctx.EOF[0]]); + + // Do not add additional line if only comments in file + const additionalLine = isNaN(compilationUnit[0].location.startOffset) + ? "" + : line; + + return concat([this.visit(compilationUnit[0]), additionalLine]); } ordinaryCompilationUnit(ctx) { @@ -29,7 +36,6 @@ class PackagesAndModulesPrettierVisitor { const staticImports = this.mapVisit(sortedImportsDecl.staticImports); const typesDecl = this.mapVisit(ctx.typeDeclaration); - // TODO: utility to add item+line (or multiple lines) but only if an item exists return rejectAndConcat([ rejectAndJoin(concat([hardline, hardline]), [ @@ -37,8 +43,7 @@ class PackagesAndModulesPrettierVisitor { rejectAndJoin(hardline, staticImports), rejectAndJoin(hardline, nonStaticImports), rejectAndJoin(concat([hardline, hardline]), typesDecl) - ]), - line + ]) ]); } @@ -54,8 +59,7 @@ class PackagesAndModulesPrettierVisitor { rejectAndJoin(hardline, staticImports), rejectAndJoin(hardline, nonStaticImports), moduleDeclaration - ]), - line + ]) ]); } @@ -220,7 +224,7 @@ class PackagesAndModulesPrettierVisitor { } requiresModifier(ctx) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } isModuleCompilationUnit(ctx) { diff --git a/packages/prettier-plugin-java/src/printers/prettier-builder.js b/packages/prettier-plugin-java/src/printers/prettier-builder.js index ed60349e..62d4bd13 100644 --- a/packages/prettier-plugin-java/src/printers/prettier-builder.js +++ b/packages/prettier-plugin-java/src/printers/prettier-builder.js @@ -1,129 +1,13 @@ "use strict"; const prettier = require("prettier").doc.builders; -const hardLineWithoutBreakParent = { type: "line", hard: true }; +const { processComments } = require("./comments"); -function getImageWithComments(token) { - return concat([ - getLeadingComments(token), - token.image, - getTrailingComments(token) - ]); -} - -function getLeadingComments(token) { - const arr = []; - if (Object.prototype.hasOwnProperty.call(token, "leadingComments")) { - token.leadingComments.forEach(element => { - if (element.startLine !== token.startLine) { - arr.push(prettier.lineSuffixBoundary); - arr.push(concat(formatComment(element))); - arr.push(hardLineWithoutBreakParent); - } else { - arr.push(concat(formatComment(element))); - } - }); - } - - return concat(arr); -} - -function getTrailingComments(token) { - const arr = []; - if (Object.prototype.hasOwnProperty.call(token, "trailingComments")) { - if (token.trailingComments[0].startLine !== token.startLine) { - arr.push(hardLineWithoutBreakParent); - } - token.trailingComments.forEach(element => { - if (element.startLine !== token.startLine) { - arr.push(concat(formatComment(element))); - arr.push(hardLineWithoutBreakParent); - } else if (element.tokenType.name === "LineComment") { - // Do not add extra space in case of empty statement - const separator = token.image === "" ? "" : " "; - arr.push( - prettier.lineSuffix( - concat([separator, concat(formatComment(element))]) - ) - ); - } else { - arr.push(concat(formatComment(element))); - } - }); - if ( - token.trailingComments[token.trailingComments.length - 1].startLine !== - token.startLine - ) { - arr.pop(); - } - } - - return concat(arr); -} - -function isJavaDoc(comment, lines) { - let isJavaDoc = true; - if (comment.tokenType.name === "TraditionalComment" && lines.length > 1) { - for (let i = 1; i < lines.length; i++) { - if (lines[i].trim().charAt(0) !== "*") { - isJavaDoc = false; - break; - } - } - } else { - isJavaDoc = false; - } - - return isJavaDoc; -} - -function formatJavaDoc(lines) { - const res = [lines[0].trim()]; - - for (let i = 1; i < lines.length; i++) { - res.push(prettier.hardline); - res.push(" " + lines[i].trim()); - } - - return res; -} - -function formatComment(comment) { - const res = []; - const lines = comment.image.split("\n"); - - if (isJavaDoc(comment, lines)) { - return formatJavaDoc(lines); - } - - lines.forEach(line => { - res.push(line); - res.push(prettier.literalline); - }); - res.pop(); - return res; -} - -function isToken(doc) { - return ( - doc && Object.prototype.hasOwnProperty.call(doc, "image") && doc.tokenType - ); -} - -function processComments(docs) { - if (!Array.isArray(docs)) { - if (isToken(docs)) { - return getImageWithComments(docs); - } - return docs; - } - return docs.map(elt => { - if (isToken(elt)) { - return getImageWithComments(elt); - } - return elt; - }); -} +/* + * ------------------------------------------------------------------ + * Wraps the Prettier builder functions to print tokens with comments + * ------------------------------------------------------------------ + */ function concat(docs) { const concatenation = prettier.concat(processComments(docs)); @@ -164,9 +48,5 @@ module.exports = { group, fill, indent, - dedent, - getImageWithComments, - getLeadingComments, - getTrailingComments, - hardLineWithoutBreakParent + dedent }; diff --git a/packages/prettier-plugin-java/src/printers/printer-utils.js b/packages/prettier-plugin-java/src/printers/printer-utils.js index 79b5265c..40292b6c 100644 --- a/packages/prettier-plugin-java/src/printers/printer-utils.js +++ b/packages/prettier-plugin-java/src/printers/printer-utils.js @@ -1,11 +1,7 @@ "use strict"; const _ = require("lodash"); -const { - join, - concat, - group, - getImageWithComments -} = require("./prettier-builder"); +const { join, concat, group } = require("./prettier-builder"); +const { printTokenWithComments } = require("./comments"); const { indent, hardline } = require("prettier").doc.builders; function buildFqn(tokens, dots) { @@ -31,6 +27,9 @@ function rejectAndJoinSeps(sepTokens, elems, sep) { function reject(elems) { return elems.filter(item => { + if (typeof item === "string") { + return item !== ""; + } // eslint-ignore next - We want the conversion to boolean! return item != false && item !== undefined; }); @@ -174,7 +173,7 @@ function findDeepElementInPartsArray(item, elt) { function displaySemicolon(token, params) { if (params !== undefined && params.allowEmptyStatement) { - return getImageWithComments(token); + return printTokenWithComments(token); } if (!hasComments(token)) { @@ -182,7 +181,7 @@ function displaySemicolon(token, params) { } token.image = ""; - return getImageWithComments(token); + return printTokenWithComments(token); } function hasLeadingComments(token) { @@ -193,6 +192,24 @@ function hasTrailingComments(token) { return token.trailingComments !== undefined; } +function hasLeadingLineComments(token) { + return ( + token.leadingComments !== undefined && + token.leadingComments.length !== 0 && + token.leadingComments[token.leadingComments.length - 1].tokenType.name === + "LineComment" + ); +} + +function hasTrailingLineComments(token) { + return ( + token.trailingComments !== undefined && + token.trailingComments.length !== 0 && + token.trailingComments[token.trailingComments.length - 1].tokenType.name === + "LineComment" + ); +} + function hasComments(token) { return hasLeadingComments(token) || hasTrailingComments(token); } @@ -475,59 +492,10 @@ function retrieveNodesTokenRec(ctx) { return tokens; } -function buildOriginalText(firstToken, lastToken, originalText) { - let startOffset = firstToken.startOffset; - let endOffset = lastToken.endOffset; - if (firstToken.leadingComments) { - startOffset = firstToken.leadingComments[0].startOffset; - } - if (lastToken.trailingComments) { - endOffset = - lastToken.trailingComments[lastToken.trailingComments.length - 1] - .endOffset; - } - return originalText.substring(startOffset, endOffset + 1); -} - -function getCSTNodeStartEndToken(ctx) { - const tokens = []; - if ( - ctx && - Object.prototype.hasOwnProperty.call(ctx, "image") && - ctx.tokenType - ) { - return [ctx, ctx]; - } - Object.keys(ctx.children).forEach(child => { - ctx.children[child].forEach(subctx => { - const subStartEndToken = getCSTNodeStartEndToken(subctx); - if (subStartEndToken) { - tokens.push(subStartEndToken); - } - }); - }); - if (tokens.length === 0) { - return; - } - const startEndTokens = tokens.reduce((tokenArr1, tokenArr2) => { - const ftoken = - tokenArr1[0].startOffset - tokenArr2[0].startOffset < 0 - ? tokenArr1[0] - : tokenArr2[0]; - const ltoken = - tokenArr2[1].startOffset - tokenArr1[1].startOffset < 0 - ? tokenArr1[1] - : tokenArr2[1]; - return [ftoken, ltoken]; - }); - return startEndTokens; -} - function isStatementEmptyStatement(statement) { return ( - statement.type === "concat" && - statement.parts[0] === "" && - statement.parts[1] === ";" + statement === ";" || + (statement.type === "concat" && statement.parts[0] === ";") ); } @@ -614,6 +582,8 @@ module.exports = { sortModifiers, rejectAndJoinSeps, findDeepElementInPartsArray, + hasLeadingLineComments, + hasTrailingLineComments, isExplicitLambdaParameter, getBlankLinesSeparator, displaySemicolon, @@ -625,8 +595,6 @@ module.exports = { separateTokensIntoGroups, isShiftOperator, retrieveNodesToken, - buildOriginalText, - getCSTNodeStartEndToken, isStatementEmptyStatement, sortImports, isUniqueMethodInvocation diff --git a/packages/prettier-plugin-java/src/printers/types-values-and-variables.js b/packages/prettier-plugin-java/src/printers/types-values-and-variables.js index c1e8e1d0..35ae8d2f 100644 --- a/packages/prettier-plugin-java/src/printers/types-values-and-variables.js +++ b/packages/prettier-plugin-java/src/printers/types-values-and-variables.js @@ -3,7 +3,8 @@ const _ = require("lodash"); -const { concat, join, getImageWithComments } = require("./prettier-builder"); +const { concat, join } = require("./prettier-builder"); +const { printTokenWithComments } = require("./comments"); const { rejectAndJoin, rejectAndConcat, @@ -26,11 +27,11 @@ class TypesValuesAndVariablesPrettierVisitor { } integralType(ctx) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } floatingPointType(ctx) { - return getImageWithComments(this.getSingle(ctx)); + return printTokenWithComments(this.getSingle(ctx)); } referenceType(ctx) { diff --git a/packages/prettier-plugin-java/test/unit-test/comments/class/_input.java b/packages/prettier-plugin-java/test/unit-test/comments/class/_input.java index 52a66b88..b3a7b6f7 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/class/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/class/_input.java @@ -795,4 +795,21 @@ public Optional getCurrentAuditor() { // See https://jira.spring.io/browse/DATACMNS-1231 return Optional.of(Constants.SYSTEM_ACCOUNT); } -} \ No newline at end of file + + // Bug Fix: #262 + @Test + @Transactional + public void getAllCountries() throws Exception { + // Initialize the database + countryRepository.saveAndFlush(country); + + // Get all the countryList + restCountryMockMvc.perform(get("/api/countries?sort=id,desc")).andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(country.getId().intValue()))) + .andExpect(jsonPath("$.[*].isoCode").value(hasItem(DEFAULT_ISO_CODE.toString()))) + .andExpect(jsonPath("$.[*].label").value(hasItem(DEFAULT_LABEL.toString()))) + .andExpect(jsonPath("$.[*].display").value(hasItem(DEFAULT_DISPLAY.booleanValue()))).andExpect( + jsonPath("$.[*].internationalDialingCode").value(hasItem(DEFAULT_INTERNATIONAL_DIALING_CODE.toString()))); + } +} diff --git a/packages/prettier-plugin-java/test/unit-test/comments/class/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/class/_output.java index 5124e0d0..d8845acd 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/class/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/class/_output.java @@ -158,8 +158,7 @@ private ArrayTable( * elements but rowKeySet() will be empty and containsRow() won't * acknolwedge them. */ - rowKeyToIndex = - Maps.indexMap(rowList); + rowKeyToIndex = Maps.indexMap(rowList); columnKeyToIndex = Maps.indexMap(columnList); @SuppressWarnings( @@ -836,4 +835,34 @@ public Optional getCurrentAuditor() { // See https://jira.spring.io/browse/DATACMNS-1231 return Optional.of(Constants.SYSTEM_ACCOUNT); } + + // Bug Fix: #262 + @Test + @Transactional + public void getAllCountries() throws Exception { + // Initialize the database + countryRepository.saveAndFlush(country); + + // Get all the countryList + restCountryMockMvc + .perform(get("/api/countries?sort=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect( + jsonPath("$.[*].id").value(hasItem(country.getId().intValue())) + ) + .andExpect( + jsonPath("$.[*].isoCode").value(hasItem(DEFAULT_ISO_CODE.toString())) + ) + .andExpect( + jsonPath("$.[*].label").value(hasItem(DEFAULT_LABEL.toString())) + ) + .andExpect( + jsonPath("$.[*].display").value(hasItem(DEFAULT_DISPLAY.booleanValue())) + ) + .andExpect( + jsonPath("$.[*].internationalDialingCode") + .value(hasItem(DEFAULT_INTERNATIONAL_DIALING_CODE.toString())) + ); + } } diff --git a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/_input.java b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/complex/_input.java similarity index 96% rename from packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/_input.java rename to packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/complex/_input.java index d5e9082e..81866194 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/complex/_input.java @@ -1,12 +1,12 @@ public class PrettierTest { - + var x=0; - + public void myFunction(int arg1){ - try { + try { ;// Empty Statement - + } /*catch*/catch ( EmptyStackException e){ throw new RuntimeException(e); } /*multi-catch*/catch ( /*1*/FirstException | /*2*/SecondException |/*3*/ ThirdException e2) @@ -16,11 +16,11 @@ public void myFunction(int arg1){ System.out.println("That's all folks !"); } } - + private void myFunction(/* axis x */ int arg1, /* axis y */ int arg2, /* axis z */int arg3){ if(arg1 == 0 && arg2 == 0 && arg == 3) throw new RuntimeException("X Y Z cannot be all 0"); - + int /*variable name is of value var */var = arg1+arg2+arg3; if/*true*/ (var == 0){ System.out.println("The value is 0"); @@ -28,14 +28,14 @@ private void myFunction(/* axis x */ int arg1, /* axis y */ int arg2, /* axis z int[] arr= { /*One*/1,/*Two */2, /*zero*/0, /*One again*/1,-1 /*Minus One*/ ,0,2}; - + loop: // Label statement - + //foreach for (int num /* num is every number in arr*/ : arr) { /*switch*/switch(num){//switch case 1: - + System.out.println("One "); System.out.println("One "); @@ -47,7 +47,7 @@ private void myFunction(/* axis x */ int arg1, /* axis y */ int arg2, /* axis z break; case 0: System.out.println("Zero "); - + continue/*labeled continued*/ loop; default/*def*/: /*labeled break*/break loop; @@ -55,9 +55,9 @@ private void myFunction(/* axis x */ int arg1, /* axis y */ int arg2, /* axis z } } } - - - + + + private synchronized void myFunction(int arg1, int arg2 /*overloading*/){ for(int i=0; i { testSample(path.resolve(__dirname, "./edge")); testSample(path.resolve(__dirname, "./interface")); testSample(path.resolve(__dirname, "./package")); - testSample(path.resolve(__dirname, "./comments-blocks-and-statements")); + testSample( + path.resolve(__dirname, "./comments-blocks-and-statements/complex") + ); + testSample( + path.resolve(__dirname, "./comments-blocks-and-statements/if-statement") + ); + testSample( + path.resolve( + __dirname, + "./comments-blocks-and-statements/labeled-statement" + ) + ); testSample(path.resolve(__dirname, "./comments-only")); testSample(path.resolve(__dirname, "./bug-fixes")); }); diff --git a/packages/prettier-plugin-java/test/unit-test/comments/interface/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/interface/_output.java index 0e4df7e5..abc18425 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/interface/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/interface/_output.java @@ -19,5 +19,5 @@ public void myMethodInterface( /*b*//*a*/Param2 /*b*//*a*/p2/*b*/, Param3 p3 ) - /*a*/throws /*b*/Exception/*a*/, /*b*/RuntimeException/*a*/;/*b*/ + /*a*/throws /*b*/Exception/*a*/, /*b*/RuntimeException /*a*/;/*b*/ } diff --git a/packages/prettier-plugin-java/test/unit-test/comments/package/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/package/_output.java index 096340d0..04769f8b 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/package/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/package/_output.java @@ -6,10 +6,8 @@ /*a*/to /*b*/another/*a*/, /*b*/again/*c*/, /*d*/ano/*a*/;/*b*/ // opens - /*a*/opens /*b*/fr.soat.vending.machine.model /*a*/to - /*b*/another/*a*/, - /*b*/again/*c*/, - /*d*/ano/*a*/;/*b*/ + /*a*/opens /*b*/fr.soat.vending.machine.model + /*a*/to /*b*/another/*a*/, /*b*/again/*c*/, /*d*/ano/*a*/;/*b*/ // uses /*a*/uses /*b*/fr.soat.vendinga/*a*/./*b*/machine.services.DrinksService/*a*/;/*b*/ diff --git a/packages/prettier-plugin-java/test/unit-test/prettier-ignore/classDeclaration/_output.java b/packages/prettier-plugin-java/test/unit-test/prettier-ignore/classDeclaration/_output.java index 436a7547..70b38d46 100644 --- a/packages/prettier-plugin-java/test/unit-test/prettier-ignore/classDeclaration/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/prettier-ignore/classDeclaration/_output.java @@ -3,4 +3,4 @@ public class PrettierIgnoreClass { public void myMethod(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, int param10) { } -} \ No newline at end of file +} diff --git a/packages/prettier-plugin-java/test/unit-test/switch/_input.java b/packages/prettier-plugin-java/test/unit-test/switch/_input.java index 8aa488a1..ebd1af46 100644 --- a/packages/prettier-plugin-java/test/unit-test/switch/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/switch/_input.java @@ -13,4 +13,34 @@ void simple(Answer answer) { } } + // Bug fix: #276 + public int method() { + switch ("abc") { + case "a": + return 1; + case "b": + return 2; + case "c": + return 3; + // default case + default: + return 3; + } + } + + // Bug fix: #276 + public int method2() { + switch ("abc") { + case "a": + return 1; + case "b": + return 2; + // case c + case "c": + return 3; + default: + return 3; + } + } + } diff --git a/packages/prettier-plugin-java/test/unit-test/switch/_output.java b/packages/prettier-plugin-java/test/unit-test/switch/_output.java index e655201c..ecd40f70 100644 --- a/packages/prettier-plugin-java/test/unit-test/switch/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/switch/_output.java @@ -12,4 +12,34 @@ void simple(Answer answer) { break; } } + + // Bug fix: #276 + public int method() { + switch ("abc") { + case "a": + return 1; + case "b": + return 2; + case "c": + return 3; + // default case + default: + return 3; + } + } + + // Bug fix: #276 + public int method2() { + switch ("abc") { + case "a": + return 1; + case "b": + return 2; + // case c + case "c": + return 3; + default: + return 3; + } + } }