From f736ecb86e633a8ff5e13f9e138b0a8a2b9dc81b Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 18 Jul 2016 14:26:31 +0200 Subject: [PATCH] [java] module formatting --- .../psi/formatter/java/AbstractJavaBlock.java | 4 +- .../psi/formatter/java/CodeBlockBlock.java | 6 +- .../java/JavaSpacePropertyProcessor.java | 59 ++++++++++++++----- .../com/intellij/psi/JavaElementVisitor.java | 12 ++-- .../psi/impl/source/tree/ElementType.java | 4 +- .../formatter/java/JavaModuleFormatterTest.kt | 39 ++++++++++++ 6 files changed, 101 insertions(+), 23 deletions(-) create mode 100644 java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaModuleFormatterTest.kt diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java index 0c4234199d2f..1343dd0834b7 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java @@ -185,7 +185,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo return new PartialWhitespaceBlock(child, range, wrap, alignment, actualIndent, settings, javaSettings); } - if (childPsi instanceof PsiClass) { + if (childPsi instanceof PsiClass || childPsi instanceof PsiJavaModule) { return new CodeBlockBlock(child, wrap, alignment, actualIndent, settings, javaSettings); } if (child.getElementType() == JavaElementType.METHOD) { @@ -203,7 +203,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo InjectedLanguageUtil.hasInjections((PsiLanguageInjectionHost)child)) { return new CommentWithInjectionBlock(child, wrap, alignment, indent, settings, javaSettings); } - if (child instanceof LeafElement) { + if (child instanceof LeafElement || childPsi instanceof PsiJavaModuleReference) { final LeafBlock block = new LeafBlock(child, wrap, alignment, actualIndent); block.setStartOffset(startOffset); return block; diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java index ddf85c69f70c..4226713c879f 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java @@ -120,7 +120,8 @@ public class CodeBlockBlock extends AbstractJavaBlock { if (child.getElementType() == JavaElementType.SWITCH_LABEL_STATEMENT) { child = processCaseAndStatementAfter(result, child, childAlignment, childWrap, indent); } - else if (myNode.getPsi() instanceof PsiClass && child.getElementType() == JavaTokenType.LBRACE) { + else if ((myNode.getPsi() instanceof PsiClass || myNode.getPsi() instanceof PsiJavaModule) && + child.getElementType() == JavaTokenType.LBRACE) { child = composeCodeBlock(result, child, getCodeBlockExternalIndent(), myChildrenIndent, null); } else if (myNode.getElementType() == JavaElementType.CODE_BLOCK && @@ -252,7 +253,8 @@ public class CodeBlockBlock extends AbstractJavaBlock { || elementType == JavaTokenType.CLASS_KEYWORD || elementType == JavaTokenType.INTERFACE_KEYWORD || elementType == JavaTokenType.IDENTIFIER - || elementType == JavaTokenType.ENUM_KEYWORD) { + || elementType == JavaTokenType.ENUM_KEYWORD + || elementType == JavaElementType.MODULE_REFERENCE) { return Indent.getNoneIndent(); } else { diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index 375d91b8f441..47e76eadcda7 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -1313,6 +1313,26 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } } + @Override + public void visitModule(PsiJavaModule module) { + if (myType2 == JavaTokenType.RBRACE || ElementType.JAVA_MODULE_STATEMENT_BIT_SET.contains(myType2)) { + myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); + } + else if (myType1 == JavaElementType.MODULE_REFERENCE || myType2 == JavaElementType.MODULE_REFERENCE) { + createSpaceInCode(true); + } + } + + @Override + public void visitModuleStatement(PsiElement statement) { + if (myType1 == JavaElementType.MODULE_REFERENCE) { + createSpaceInCode(myType2 != JavaTokenType.SEMICOLON && myType2 != JavaTokenType.COMMA); + } + if (myType2 == JavaElementType.MODULE_REFERENCE) { + createSpaceInCode(true); + } + } + @Override public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) { if ((myRole1 == ChildRole.DOUBLE_COLON && myRole2 == ChildRole.REFERENCE_NAME) || @@ -1768,25 +1788,36 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { Pair pair = Pair.create(type1, type2); Boolean res = myCanStickJavaTokensMatrix.get(pair); if (res == null) { - if (!checkToken(token1) || !checkToken(token2)) return true; - String text = token1.getText() + token2.getText(); Lexer lexer = JavaParserDefinition.createLexer(LanguageLevel.HIGHEST); - lexer.start(text); - boolean canMerge = lexer.getTokenType() == type1; - lexer.advance(); - canMerge &= lexer.getTokenType() == type2; - res = canMerge; + + TokenCheckResult res1 = checkToken(token1, lexer), res2 = checkToken(token2, lexer); + if (res1 == TokenCheckResult.INCORRECT || res2 == TokenCheckResult.INCORRECT) return true; + + if (res1 == TokenCheckResult.RESTRICTED_KEYWORD || type1 == JavaTokenType.IDENTIFIER && res2 == TokenCheckResult.RESTRICTED_KEYWORD) { + res = false; + } + else { + lexer.start(token1.getText() + token2.getText()); + boolean canMerge = lexer.getTokenType() == type1; + lexer.advance(); + canMerge &= lexer.getTokenType() == type2; + res = canMerge; + } + myCanStickJavaTokensMatrix.put(pair, res); } return res.booleanValue(); } - private static boolean checkToken(final ASTNode token1) { - Lexer lexer = JavaParserDefinition.createLexer(LanguageLevel.HIGHEST); - final String text = token1.getText(); - lexer.start(text); - if (lexer.getTokenType() != token1.getElementType()) return false; + private enum TokenCheckResult {OK, INCORRECT, RESTRICTED_KEYWORD} + + private static TokenCheckResult checkToken(ASTNode token, Lexer lexer) { + lexer.start(token.getText()); + if (lexer.getTokenType() != token.getElementType()) { + boolean kw = lexer.getTokenType() == JavaTokenType.IDENTIFIER && ElementType.KEYWORD_BIT_SET.contains(token.getElementType()); + return kw ? TokenCheckResult.RESTRICTED_KEYWORD : TokenCheckResult.INCORRECT; + } lexer.advance(); - return lexer.getTokenType() == null; + return lexer.getTokenType() == null ? TokenCheckResult.OK : TokenCheckResult.INCORRECT; } -} +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java b/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java index be4d6b5e6fb2..a0b0ea22d8f2 100644 --- a/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java +++ b/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java @@ -395,19 +395,23 @@ public abstract class JavaElementVisitor extends PsiElementVisitor { visitElement(moduleRef); } - public void visitRequiresStatement(PsiRequiresStatement statement) { + public void visitModuleStatement(PsiElement statement) { visitElement(statement); } + public void visitRequiresStatement(PsiRequiresStatement statement) { + visitModuleStatement(statement); + } + public void visitExportsStatement(PsiExportsStatement statement) { - visitElement(statement); + visitModuleStatement(statement); } public void visitUsesStatement(PsiUsesStatement statement) { - visitElement(statement); + visitModuleStatement(statement); } public void visitProvidesStatement(PsiProvidesStatement statement) { - visitElement(statement); + visitModuleStatement(statement); } } \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java index 41ee37015198..953505b35c1c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java @@ -70,8 +70,10 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, JavaElemen WHILE_STATEMENT, FOR_STATEMENT, FOREACH_STATEMENT, DO_WHILE_STATEMENT, SWITCH_STATEMENT, SWITCH_LABEL_STATEMENT, BREAK_STATEMENT, CONTINUE_STATEMENT, RETURN_STATEMENT, THROW_STATEMENT, SYNCHRONIZED_STATEMENT, TRY_STATEMENT, LABELED_STATEMENT, ASSERT_STATEMENT); + TokenSet JAVA_MODULE_STATEMENT_BIT_SET = TokenSet.create(REQUIRES_STATEMENT, EXPORTS_STATEMENT, USES_STATEMENT, PROVIDES_STATEMENT); + TokenSet IMPORT_STATEMENT_BASE_BIT_SET = TokenSet.create(IMPORT_STATEMENT, IMPORT_STATIC_STATEMENT); TokenSet CLASS_KEYWORD_BIT_SET = TokenSet.create(CLASS_KEYWORD, INTERFACE_KEYWORD, ENUM_KEYWORD); TokenSet MEMBER_BIT_SET = TokenSet.create(CLASS, FIELD, ENUM_CONSTANT, METHOD, ANNOTATION_METHOD); TokenSet FULL_MEMBER_BIT_SET = TokenSet.orSet(MEMBER_BIT_SET, TokenSet.create(CLASS_INITIALIZER)); -} +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaModuleFormatterTest.kt b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaModuleFormatterTest.kt new file mode 100644 index 000000000000..7cae4b3b9b98 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaModuleFormatterTest.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.formatter.java + +import com.intellij.openapi.roots.LanguageLevelProjectExtension +import com.intellij.pom.java.LanguageLevel +import com.intellij.testFramework.LightPlatformTestCase + +class JavaModuleFormatterTest : AbstractJavaFormatterTest() { + override fun setUp() { + super.setUp() + LanguageLevelProjectExtension.getInstance(LightPlatformTestCase.getProject()).languageLevel = LanguageLevel.JDK_1_9 + } + + fun testEmpty() { + doTextTest("module A.B { }", "module A.B {\n}") + } + + fun testCommentBody() { + doTextTest("module m { /* comment */ }", "module m { /* comment */\n}") + } + + fun testStatements() { + doTextTest("module m { requires java.base; exports a.b; }", "module m {\n requires java.base;\n exports a.b;\n}") + } +} \ No newline at end of file