From a5c7e832df6ee0b207081f317b180d8c0dffc168 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 18 Feb 2011 15:07:36 +0100 Subject: [PATCH 01/38] fix test data? --- .../com/intellij/projectView/NavigateFromSourceTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java b/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java index d3597d29cf4b..6c41be62867f 100644 --- a/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java +++ b/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java @@ -72,7 +72,8 @@ public class NavigateFromSourceTest extends BaseProjectViewTestCase { " -PsiDirectory: src\n" + " -PsiDirectory: com\n" + " -PsiDirectory: package1\n" + - " [Class11]\n" + + " -[Class1.java]\n" + + " [Class11]\n" + " Class2\n" + getRootFiles() + " +External Libraries\n"); From 2087ecd0f6ee2bb208c1427e256b945b61854264 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 18 Feb 2011 15:11:19 +0100 Subject: [PATCH 02/38] a truly right alignment for the type text --- .../src/com/intellij/codeInsight/lookup/impl/LookupImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index 11e5bafcc6eb..d12bd938be93 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java @@ -1187,6 +1187,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { c.gridy = i; c.ipadx = itemTextPadding; c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1.0; { From 409d194aa2743bd7c60ec75e2146a71ddcd0cb78 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 18 Feb 2011 10:58:10 +0100 Subject: [PATCH 03/38] To JB dictionary --- plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic b/plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic index 5e4a963686c2..79d88ab70168 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic +++ b/plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic @@ -314,6 +314,7 @@ versa vertices webservice whitespace +whitespaces wildcard workflow wsdl From 40046e9177036eaea03192a8ebeeb1ec16997ae2 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 18 Feb 2011 14:10:58 +0100 Subject: [PATCH 04/38] Project Coin try-with-resource support (parser, PSI) --- .../lang/java/parser/DeclarationParser.java | 59 +++++++++++++------ .../lang/java/parser/JavaParserUtil.java | 6 +- .../lang/java/parser/StatementParser.java | 12 +++- .../com/intellij/psi/impl/PsiImplUtil.java | 38 +++++++++--- .../psi/impl/PsiJavaParserFacadeImpl.java | 17 ++++-- .../psi/impl/source/JavaLightStubBuilder.java | 7 +-- .../source/tree/java/PsiTryStatementImpl.java | 29 +++++++-- .../statements/TryIncomplete10.txt | 10 ++++ .../statements/TryIncomplete11.txt | 11 ++++ .../statements/TryIncomplete12.txt | 14 +++++ .../statements/TryIncomplete13.txt | 18 ++++++ .../statements/TryIncomplete14.txt | 18 ++++++ .../statements/TryIncomplete15.txt | 21 +++++++ .../statements/TryIncomplete16.txt | 23 ++++++++ .../statements/TryIncomplete17.txt | 27 +++++++++ .../statements/TryIncomplete8.txt | 25 ++++++++ .../statements/TryIncomplete9.txt | 28 +++++++++ .../parser-partial/statements/TryNormal1.txt | 7 ++- .../parser-partial/statements/TryNormal3.txt | 30 ++++++++++ .../parser-partial/statements/TryNormal4.txt | 24 ++++++++ .../parser-partial/statements/TryNormal5.txt | 41 +++++++++++++ .../parser/partial/StatementParserTest.java | 25 +++++++- .../com/intellij/psi/JavaStubBuilderTest.java | 22 +++++++ .../src/com/intellij/psi/PsiTryStatement.java | 8 +++ .../src/messages/JavaErrorMessages.properties | 1 + 25 files changed, 471 insertions(+), 50 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete10.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete11.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete12.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete13.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete14.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete15.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete16.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete17.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete8.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete9.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryNormal3.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryNormal4.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryNormal5.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 606826da384a..23a23791a1aa 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -447,26 +447,43 @@ public class DeclarationParser { @NotNull public static PsiBuilder.Marker parseParameterList(final PsiBuilder builder) { + return parseParameterList(builder, false); + } + + @NotNull + public static PsiBuilder.Marker parseResourceList(final PsiBuilder builder) { + return parseParameterList(builder, true); + } + + @NotNull + private static PsiBuilder.Marker parseParameterList(final PsiBuilder builder, final boolean resources) { assert builder.getTokenType() == JavaTokenType.LPARENTH : builder.getTokenType(); final PsiBuilder.Marker paramList = builder.mark(); builder.advanceLexer(); + final IElementType delimiter = resources ? JavaTokenType.SEMICOLON : JavaTokenType.COMMA; + final String noDelimiterMsg = JavaErrorMessages.message(resources ? "expected.semicolon" : "expected.comma"); + final String noParameterMsg = JavaErrorMessages.message(resources ? "expected.resource" : "expected.parameter"); + PsiBuilder.Marker invalidElements = null; String errorMessage = null; - boolean commaExpected = false; + boolean delimiterExpected = false; int paramCount = 0; while (true) { final IElementType tokenType = builder.getTokenType(); - if (tokenType == null || tokenType == JavaTokenType.RPARENTH) { - boolean noLastParam = !commaExpected && paramCount > 0; + if (tokenType == null || tokenType == JavaTokenType.RPARENTH || tokenType == JavaTokenType.LBRACE) { + boolean noLastParam = !delimiterExpected && paramCount > 0; if (noLastParam) { error(builder, JavaErrorMessages.message("expected.identifier.or.type")); } if (tokenType == JavaTokenType.RPARENTH) { if (invalidElements != null) { invalidElements.error(errorMessage); + invalidElements = null; + } + else if (resources && paramCount == 0) { + error(builder, JavaErrorMessages.message("expected.resource")); } - invalidElements = null; builder.advanceLexer(); } else { @@ -481,9 +498,9 @@ public class DeclarationParser { break; } - if (commaExpected) { - if (builder.getTokenType() == JavaTokenType.COMMA) { - commaExpected = false; + if (delimiterExpected) { + if (builder.getTokenType() == delimiter) { + delimiterExpected = false; if (invalidElements != null) { invalidElements.error(errorMessage); invalidElements = null; @@ -493,9 +510,9 @@ public class DeclarationParser { } } else { - final PsiBuilder.Marker param = parseParameter(builder, true, false); + final PsiBuilder.Marker param = parseParameter(builder, true, false, resources); if (param != null) { - commaExpected = true; + delimiterExpected = true; if (invalidElements != null) { invalidElements.errorBefore(errorMessage, param); invalidElements = null; @@ -506,14 +523,14 @@ public class DeclarationParser { } if (invalidElements == null) { - if (builder.getTokenType() == JavaTokenType.COMMA) { - error(builder, JavaErrorMessages.message("expected.parameter")); + if (builder.getTokenType() == delimiter) { + error(builder, noParameterMsg); builder.advanceLexer(); continue; } else { invalidElements = builder.mark(); - errorMessage = commaExpected ? JavaErrorMessages.message("expected.comma") : JavaErrorMessages.message("expected.parameter"); + errorMessage = delimiterExpected ? noDelimiterMsg : noParameterMsg; } } @@ -533,14 +550,14 @@ public class DeclarationParser { } @Nullable - public static PsiBuilder.Marker parseParameter(final PsiBuilder builder, final boolean ellipsis, final boolean disjunctiveType) { + public static PsiBuilder.Marker parseParameter(final PsiBuilder builder, final boolean ellipsis, final boolean disjunction, final boolean value) { final PsiBuilder.Marker param = builder.mark(); final Pair modListInfo = parseModifierList(builder); int flags = ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD; if (ellipsis) flags |= ReferenceParser.ELLIPSIS; - if (disjunctiveType) flags |= ReferenceParser.DISJUNCTIONS; + if (disjunction) flags |= ReferenceParser.DISJUNCTIONS; final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseTypeInfo(builder, flags); if (typeInfo == null) { @@ -555,15 +572,23 @@ public class DeclarationParser { } if (expect(builder, JavaTokenType.IDENTIFIER)) { - eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, JavaErrorMessages.message("expected.rparen")); - done(param, JavaElementType.PARAMETER); - return param; + eatBrackets(builder, typeInfo != null && typeInfo.isVarArg || value, JavaErrorMessages.message("expected.rparen")); + if (value) { + if (expectOrError(builder, JavaTokenType.EQ, JavaErrorMessages.message("expected.eq"))) { + if (ExpressionParser.parse(builder) == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + } + } + } } else { error(builder, JavaErrorMessages.message("expected.identifier")); param.drop(); return modListInfo.first; } + + done(param, JavaElementType.PARAMETER); + return param; } @Nullable diff --git a/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java b/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java index 2aac0a46390d..69717a1769f3 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java @@ -138,14 +138,16 @@ public class JavaParserUtil { builder.putUserDataUnprotected(LANG_LEVEL_KEY, level); } + // todo[r.sh] join all JDK 7 check clauses into single method (IDEA 11) public static boolean areDiamondsSupported(final PsiBuilder builder) { return getLanguageLevel(builder).isAtLeast(LanguageLevel.JDK_1_7); } - - // todo[r.sh] join all JDK 7 check clauses into single method (IDEA 11) public static boolean areMultiCatchSupported(final PsiBuilder builder) { return getLanguageLevel(builder).isAtLeast(LanguageLevel.JDK_1_7); } + public static boolean areTryWithResourcesSupported(final PsiBuilder builder) { + return getLanguageLevel(builder).isAtLeast(LanguageLevel.JDK_1_7); + } public static boolean areTypeAnnotationsSupported(final PsiBuilder builder) { return getLanguageLevel(builder).isAtLeast(LanguageLevel.JDK_1_8); diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index a4ba10e2e8b0..eb10691aca69 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -375,7 +375,7 @@ public class StatementParser { } final PsiBuilder.Marker afterParenth = builder.mark(); - final PsiBuilder.Marker param = DeclarationParser.parseParameter(builder, false, false); + final PsiBuilder.Marker param = DeclarationParser.parseParameter(builder, false, false, false); if (param == null || JavaParserUtil.exprType(param) != JavaElementType.PARAMETER || builder.getTokenType() != JavaTokenType.COLON) { afterParenth.rollbackTo(); return parseForLoopFromInitialization(builder, statement); @@ -612,6 +612,12 @@ public class StatementParser { final PsiBuilder.Marker statement = builder.mark(); builder.advanceLexer(); + boolean hasResourceList = false; + if (areTryWithResourcesSupported(builder) && builder.getTokenType() == JavaTokenType.LPARENTH) { + hasResourceList = true; + DeclarationParser.parseResourceList(builder); + } + final PsiBuilder.Marker tryBlock = parseCodeBlock(builder, true); if (tryBlock == null) { error(builder, JavaErrorMessages.message("expected.lbrace")); @@ -619,7 +625,7 @@ public class StatementParser { return statement; } - if (!TRY_CLOSERS_SET.contains(builder.getTokenType())) { + if (!hasResourceList && !TRY_CLOSERS_SET.contains(builder.getTokenType())) { error(builder, JavaErrorMessages.message("expected.catch.or.finally")); done(statement, JavaElementType.TRY_STATEMENT); return statement; @@ -651,7 +657,7 @@ public class StatementParser { return false; } - final PsiBuilder.Marker param = DeclarationParser.parseParameter(builder, false, areMultiCatchSupported(builder)); + final PsiBuilder.Marker param = DeclarationParser.parseParameter(builder, false, areMultiCatchSupported(builder), false); if (param == null) { error(builder, JavaErrorMessages.message("expected.parameter")); } diff --git a/java/java-impl/src/com/intellij/psi/impl/PsiImplUtil.java b/java/java-impl/src/com/intellij/psi/impl/PsiImplUtil.java index 70fc4c6e769d..c2a7fa637c26 100644 --- a/java/java-impl/src/com/intellij/psi/impl/PsiImplUtil.java +++ b/java/java-impl/src/com/intellij/psi/impl/PsiImplUtil.java @@ -173,11 +173,11 @@ public class PsiImplUtil { return processor.getResults().toArray(); } - public static boolean processDeclarationsInMethod(PsiMethod method, - @NotNull PsiScopeProcessor processor, - ResolveState state, - PsiElement lastParent, - PsiElement place) { + public static boolean processDeclarationsInMethod(@NotNull final PsiMethod method, + @NotNull final PsiScopeProcessor processor, + @NotNull final ResolveState state, + final PsiElement lastParent, + @NotNull final PsiElement place) { final ElementClassHint hint = processor.getHint(ElementClassHint.KEY); processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, method); if (hint == null || hint.shouldProcess(ElementClassHint.DeclaractionKind.CLASS)) { @@ -185,15 +185,35 @@ public class PsiImplUtil { if (list != null && !list.processDeclarations(processor, state, null, place)) return false; } if (lastParent instanceof PsiCodeBlock) { - final PsiParameter[] parameters = method.getParameterList().getParameters(); - for (PsiParameter parameter : parameters) { - if (!processor.execute(parameter, state)) return false; - } + final PsiParameterList parameterList = method.getParameterList(); + if (processDeclarationsInParameterList(parameterList, processor, state)) return false; } return true; } + public static boolean processDeclarationsInTryStatement(@NotNull final PsiTryStatement statement, + @NotNull final PsiScopeProcessor processor, + @NotNull final ResolveState state, + final PsiElement lastParent) { + if (lastParent instanceof PsiCodeBlock) { + final PsiParameterList parameterList = statement.getResourceList(); + if (parameterList != null && processDeclarationsInParameterList(parameterList, processor, state)) return false; + } + + return true; + } + + private static boolean processDeclarationsInParameterList(@NotNull final PsiParameterList parameterList, + @NotNull final PsiScopeProcessor processor, + @NotNull final ResolveState state) { + final PsiParameter[] parameters = parameterList.getParameters(); + for (PsiParameter parameter : parameters) { + if (!processor.execute(parameter, state)) return true; + } + return false; + } + public static boolean hasTypeParameters(@NotNull PsiTypeParameterListOwner owner) { final PsiTypeParameterList typeParameterList = owner.getTypeParameterList(); return typeParameterList != null && typeParameterList.getTypeParameters().length != 0; diff --git a/java/java-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java b/java/java-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java index 04f4756ffd93..083602d0e83f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.psi.impl; import com.intellij.ide.fileTemplates.FileTemplate; @@ -45,7 +44,7 @@ import java.util.Properties; import static com.intellij.openapi.util.text.StringUtil.join; -/* +/** * @author max */ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJavaParserFacade { @@ -61,7 +60,14 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ private static final JavaParserUtil.ParserWrapper PARAMETER = new JavaParserUtil.ParserWrapper() { @Override public void parse(final PsiBuilder builder) { - DeclarationParser.parseParameter(builder, true, false); + DeclarationParser.parseParameter(builder, true, false, false); + } + }; + + private static final JavaParserUtil.ParserWrapper RESOURCE = new JavaParserUtil.ParserWrapper() { + @Override + public void parse(final PsiBuilder builder) { + DeclarationParser.parseParameter(builder, true, false, true); } }; @@ -233,7 +239,10 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ @NotNull public PsiParameter createParameterFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException { - final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, PARAMETER, false), context); + final boolean resource = context instanceof PsiParameterList && + context.getParent() instanceof PsiTryStatement; + final JavaParserUtil.ParserWrapper wrapper = resource ? RESOURCE : PARAMETER; + final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, wrapper, false), context); final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); if (!(element instanceof PsiParameter)) { throw new IncorrectOperationException("Incorrect parameter \"" + text + "\"."); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/JavaLightStubBuilder.java b/java/java-impl/src/com/intellij/psi/impl/source/JavaLightStubBuilder.java index 76800f5db135..55d28120e863 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/JavaLightStubBuilder.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/JavaLightStubBuilder.java @@ -49,10 +49,7 @@ public class JavaLightStubBuilder extends LightStubBuilder { @Override public boolean skipChildProcessingWhenBuildingStubs(final IElementType nodeType, final IElementType childType) { - if (childType == JavaElementType.PARAMETER && nodeType != JavaElementType.PARAMETER_LIST) { - return true; - } - - return false; + return childType == JavaElementType.PARAMETER_LIST && nodeType != JavaElementType.METHOD && nodeType != JavaElementType.ANNOTATION_METHOD || + childType == JavaElementType.PARAMETER && nodeType != JavaElementType.PARAMETER_LIST; } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiTryStatementImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiTryStatementImpl.java index 98689fcdc78d..dc23a38cd074 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiTryStatementImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiTryStatementImpl.java @@ -18,29 +18,33 @@ package com.intellij.psi.impl.source.tree.java; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; -import com.intellij.psi.impl.source.SourceTreeToPsiMap; +import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.source.Constants; +import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.impl.source.tree.ChildRole; import com.intellij.psi.impl.source.tree.CompositePsiElement; -import com.intellij.psi.tree.IElementType; +import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.tree.ChildRoleBase; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; public class PsiTryStatementImpl extends CompositePsiElement implements PsiTryStatement, Constants { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiTryStatementImpl"); + private volatile PsiParameter[] myCachedCatchParameters = null; + public PsiTryStatementImpl() { + super(TRY_STATEMENT); + } + public void clearCaches() { super.clearCaches(); myCachedCatchParameters = null; } - public PsiTryStatementImpl() { - super(TRY_STATEMENT); - } - public PsiCodeBlock getTryBlock() { return (PsiCodeBlock)findChildByRoleAsPsiElement(ChildRole.TRY_BLOCK); } @@ -88,6 +92,11 @@ public class PsiTryStatementImpl extends CompositePsiElement implements PsiTrySt return (PsiCodeBlock)findChildByRoleAsPsiElement(ChildRole.FINALLY_BLOCK); } + @Override + public PsiParameterList getResourceList() { + return PsiTreeUtil.findChildOfType(this, PsiParameterList.class); + } + public ASTNode findChildByRole(int role) { LOG.assertTrue(ChildRole.isUnique(role)); switch(role){ @@ -150,6 +159,14 @@ public class PsiTryStatementImpl extends CompositePsiElement implements PsiTrySt } } + @Override + public boolean processDeclarations(@NotNull final PsiScopeProcessor processor, + @NotNull final ResolveState state, + final PsiElement lastParent, + @NotNull final PsiElement place) { + return PsiImplUtil.processDeclarationsInTryStatement(this, processor, state, lastParent); + } + public String toString() { return "PsiTryStatement"; } diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete10.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete10.txt new file mode 100644 index 000000000000..c9b9aaf68b6e --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete10.txt @@ -0,0 +1,10 @@ +PsiJavaFile:TryIncomplete10.java + PsiTryStatement + PsiKeyword:try('try') + PsiParameterList:( + PsiJavaToken:LPARENTH('(') + PsiErrorElement:')' expected + + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete11.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete11.txt new file mode 100644 index 000000000000..05e296fcc190 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete11.txt @@ -0,0 +1,11 @@ +PsiJavaFile:TryIncomplete11.java + PsiTryStatement + PsiKeyword:try('try') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Resource definition expected + + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete12.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete12.txt new file mode 100644 index 000000000000..829a9a293c17 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete12.txt @@ -0,0 +1,14 @@ +PsiJavaFile:TryIncomplete12.java + PsiTryStatement + PsiKeyword:try('try') + PsiParameterList:(;) + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Resource definition expected + + PsiJavaToken:SEMICOLON(';') + PsiErrorElement:Resource definition expected + + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete13.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete13.txt new file mode 100644 index 000000000000..5883e80baf09 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete13.txt @@ -0,0 +1,18 @@ +PsiJavaFile:TryIncomplete13.java + PsiTryStatement + PsiKeyword:try('try') + PsiParameterList:(final ) + PsiJavaToken:LPARENTH('(') + PsiModifierList:final + PsiKeyword:final('final') + PsiErrorElement:Type expected + + PsiWhiteSpace(' ') + PsiTypeElement: + + PsiErrorElement:Identifier expected + + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete14.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete14.txt new file mode 100644 index 000000000000..36386acc63b2 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete14.txt @@ -0,0 +1,18 @@ +PsiJavaFile:TryIncomplete14.java + PsiTryStatement + PsiKeyword:try('try') + PsiParameterList:(R) + PsiJavaToken:LPARENTH('(') + PsiModifierList: + + PsiTypeElement:R + PsiJavaCodeReferenceElement:R + PsiIdentifier:R('R') + PsiReferenceParameterList + + PsiErrorElement:Identifier expected + + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete15.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete15.txt new file mode 100644 index 000000000000..f38fe8b04bc7 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete15.txt @@ -0,0 +1,21 @@ +PsiJavaFile:TryIncomplete15.java + PsiTryStatement + PsiKeyword:try('try') + PsiParameterList:(R r) + PsiJavaToken:LPARENTH('(') + PsiParameter:r + PsiModifierList: + + PsiTypeElement:R + PsiJavaCodeReferenceElement:R + PsiIdentifier:R('R') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:r('r') + PsiErrorElement:'=' expected + + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete16.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete16.txt new file mode 100644 index 000000000000..d18696953396 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete16.txt @@ -0,0 +1,23 @@ +PsiJavaFile:TryIncomplete16.java + PsiTryStatement + PsiKeyword:try('try') + PsiParameterList:(R r =) + PsiJavaToken:LPARENTH('(') + PsiParameter:r + PsiModifierList: + + PsiTypeElement:R + PsiJavaCodeReferenceElement:R + PsiIdentifier:R('R') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:r('r') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiErrorElement:Expression expected + + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete17.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete17.txt new file mode 100644 index 000000000000..4f8626c450a0 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete17.txt @@ -0,0 +1,27 @@ +PsiJavaFile:TryIncomplete17.java + PsiTryStatement + PsiKeyword:try('try') + PsiParameterList:(R r = 0;) + PsiJavaToken:LPARENTH('(') + PsiParameter:r + PsiModifierList: + + PsiTypeElement:R + PsiJavaCodeReferenceElement:R + PsiIdentifier:R('R') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:r('r') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiErrorElement:Identifier or type expected + + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete8.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete8.txt new file mode 100644 index 000000000000..da0b1f4548e8 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete8.txt @@ -0,0 +1,25 @@ +PsiJavaFile:TryIncomplete8.java + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiCatchSection + PsiKeyword:catch('catch') + PsiJavaToken:LPARENTH('(') + PsiModifierList: + + PsiTypeElement:A| + PsiTypeElement:A + PsiJavaCodeReferenceElement:A + PsiIdentifier:A('A') + PsiReferenceParameterList + + PsiJavaToken:OR('|') + PsiErrorElement:Identifier expected + + PsiErrorElement:Identifier expected + + PsiJavaToken:RPARENTH(')') + PsiErrorElement:'{' expected + diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete9.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete9.txt new file mode 100644 index 000000000000..8ca57aa18940 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete9.txt @@ -0,0 +1,28 @@ +PsiJavaFile:TryIncomplete9.java + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiCatchSection + PsiKeyword:catch('catch') + PsiJavaToken:LPARENTH('(') + PsiModifierList: + + PsiTypeElement:A|B + PsiTypeElement:A + PsiJavaCodeReferenceElement:A + PsiIdentifier:A('A') + PsiReferenceParameterList + + PsiJavaToken:OR('|') + PsiTypeElement:B + PsiJavaCodeReferenceElement:B + PsiIdentifier:B('B') + PsiReferenceParameterList + + PsiErrorElement:Identifier expected + + PsiJavaToken:RPARENTH(')') + PsiErrorElement:'{' expected + diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal1.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal1.txt index 71df6c432448..9bf4dc6a467b 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryNormal1.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal1.txt @@ -8,8 +8,9 @@ PsiJavaFile:TryNormal1.java PsiKeyword:catch('catch') PsiJavaToken:LPARENTH('(') PsiParameter:e - PsiModifierList: - + PsiModifierList:final + PsiKeyword:final('final') + PsiWhiteSpace(' ') PsiTypeElement:E PsiJavaCodeReferenceElement:E PsiIdentifier:E('E') @@ -24,4 +25,4 @@ PsiJavaFile:TryNormal1.java PsiKeyword:finally('finally') PsiCodeBlock PsiJavaToken:LBRACE('{') - PsiJavaToken:RBRACE('}') \ No newline at end of file + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal3.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal3.txt new file mode 100644 index 000000000000..96a06ad6e65a --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal3.txt @@ -0,0 +1,30 @@ +PsiJavaFile:TryNormal3.java + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiCatchSection + PsiKeyword:catch('catch') + PsiJavaToken:LPARENTH('(') + PsiParameter:e + PsiModifierList: + + PsiTypeElement:A|B + PsiTypeElement:A + PsiJavaCodeReferenceElement:A + PsiIdentifier:A('A') + PsiReferenceParameterList + + PsiJavaToken:OR('|') + PsiTypeElement:B + PsiJavaCodeReferenceElement:B + PsiIdentifier:B('B') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:e('e') + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal4.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal4.txt new file mode 100644 index 000000000000..29b541335e1f --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal4.txt @@ -0,0 +1,24 @@ +PsiJavaFile:TryNormal4.java + PsiTryStatement + PsiKeyword:try('try') + PsiParameterList:(R r = 0) + PsiJavaToken:LPARENTH('(') + PsiParameter:r + PsiModifierList: + + PsiTypeElement:R + PsiJavaCodeReferenceElement:R + PsiIdentifier:R('R') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:r('r') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal5.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal5.txt new file mode 100644 index 000000000000..78c53a180d3c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal5.txt @@ -0,0 +1,41 @@ +PsiJavaFile:TryNormal5.java + PsiTryStatement + PsiKeyword:try('try') + PsiParameterList:(R1 r1 = 1; R2 r2 = 2) + PsiJavaToken:LPARENTH('(') + PsiParameter:r1 + PsiModifierList: + + PsiTypeElement:R1 + PsiJavaCodeReferenceElement:R1 + PsiIdentifier:R1('R1') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:r1('r1') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:1 + PsiJavaToken:INTEGER_LITERAL('1') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiParameter:r2 + PsiModifierList: + + PsiTypeElement:R2 + PsiJavaCodeReferenceElement:R2 + PsiIdentifier:R2('R2') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:r2('r2') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:2 + PsiJavaToken:INTEGER_LITERAL('2') + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java index 48bb7b8ce083..7cf68cf63d66 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java @@ -18,6 +18,7 @@ package com.intellij.lang.java.parser.partial; import com.intellij.lang.PsiBuilder; import com.intellij.lang.java.parser.JavaParsingTestCase; import com.intellij.lang.java.parser.StatementParser; +import com.intellij.pom.java.LanguageLevel; public class StatementParserTest extends JavaParsingTestCase { @@ -116,8 +117,11 @@ public class StatementParserTest extends JavaParsingTestCase { public void testThrowIncomplete1() { doParserTest("throw e"); } public void testTryNormal0() { doParserTest("try{}catch(E e){}"); } - public void testTryNormal1() { doParserTest("try{}catch(E e){}finally{}"); } + public void testTryNormal1() { doParserTest("try{}catch(final E e){}finally{}"); } public void testTryNormal2() { doParserTest("try{}finally{}"); } + public void testTryNormal3() { doParserTestJDK7("try{}catch(A|B e){}"); } + public void testTryNormal4() { doParserTestJDK7("try(R r = 0){}"); } + public void testTryNormal5() { doParserTestJDK7("try(R1 r1 = 1; R2 r2 = 2){}"); } public void testTryIncomplete0() { doParserTest("try"); } public void testTryIncomplete1() { doParserTest("try{}"); } public void testTryIncomplete2() { doParserTest("try{}catch"); } @@ -126,6 +130,16 @@ public class StatementParserTest extends JavaParsingTestCase { public void testTryIncomplete5() { doParserTest("try{}catch(E e"); } public void testTryIncomplete6() { doParserTest("try{}catch(E e)"); } public void testTryIncomplete7() { doParserTest("try{}finally"); } + public void testTryIncomplete8() { doParserTestJDK7("try{}catch(A|)"); } + public void testTryIncomplete9() { doParserTestJDK7("try{}catch(A|B)"); } + public void testTryIncomplete10() { doParserTestJDK7("try({}"); } + public void testTryIncomplete11() { doParserTestJDK7("try(){}"); } + public void testTryIncomplete12() { doParserTestJDK7("try(;){}"); } + public void testTryIncomplete13() { doParserTestJDK7("try(final ){}"); } + public void testTryIncomplete14() { doParserTestJDK7("try(R){}"); } + public void testTryIncomplete15() { doParserTestJDK7("try(R r){}"); } + public void testTryIncomplete16() { doParserTestJDK7("try(R r =){}"); } + public void testTryIncomplete17() { doParserTestJDK7("try(R r = 0;){}"); } public void testWhileNormal() { doParserTest("while (true) foo();"); } public void testWhileIncomplete0() { doParserTest("while"); } @@ -152,4 +166,13 @@ public class StatementParserTest extends JavaParsingTestCase { } }); } + + private void doParserTestJDK7(final String text) { + withLevel(LanguageLevel.JDK_1_7, new Runnable() { + @Override + public void run() { + doParserTest(text); + } + }); + } } diff --git a/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java b/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java index 4108ff868342..79d1c17fcc8f 100644 --- a/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java @@ -296,6 +296,28 @@ public class JavaStubBuilderTest extends LightIdeaTestCase { }); } + public void testNonMethodParameterLists() { + withLevel(LanguageLevel.JDK_1_7, new Runnable() { + @Override public void run() { + doTest("class C {\n" + + " {\n" + + " try (Resource r = new Resource()) { }\n" + + " }\n" + + "}", + + "PsiJavaFileStub []\n" + + " IMPORT_LIST:PsiImportListStub\n" + + " CLASS:PsiClassStub[name=C fqn=C]\n" + + " MODIFIER_LIST:PsiModifierListStub[mask=4096]\n" + + " TYPE_PARAMETER_LIST:PsiTypeParameterListStub\n" + + " EXTENDS_LIST:PsiRefListStub[EXTENDS_LIST:]\n" + + " IMPLEMENTS_LIST:PsiRefListStub[IMPLEMENTS_LIST:]\n" + + " CLASS_INITIALIZER:PsiClassInitializerStub\n" + + " MODIFIER_LIST:PsiModifierListStub[mask=4096]\n"); + } + }); + } + public void testSOEProof() { final StringBuilder sb = new StringBuilder(); final SecureRandom random = new SecureRandom(); diff --git a/java/openapi/src/com/intellij/psi/PsiTryStatement.java b/java/openapi/src/com/intellij/psi/PsiTryStatement.java index a74ae8d52e61..91375d343340 100644 --- a/java/openapi/src/com/intellij/psi/PsiTryStatement.java +++ b/java/openapi/src/com/intellij/psi/PsiTryStatement.java @@ -64,4 +64,12 @@ public interface PsiTryStatement extends PsiStatement { */ @Nullable PsiCodeBlock getFinallyBlock(); + + /** + * Returns a resource list of try-with-resources statement. + * + * @return resource list, or null if the statement doesn't have it. + */ + @Nullable + PsiParameterList getResourceList(); } diff --git a/resources-en/src/messages/JavaErrorMessages.properties b/resources-en/src/messages/JavaErrorMessages.properties index c7d72c56649a..cefabbeed699 100644 --- a/resources-en/src/messages/JavaErrorMessages.properties +++ b/resources-en/src/messages/JavaErrorMessages.properties @@ -310,6 +310,7 @@ unexpected.identifier=Unexpected identifier expected.gt='>' expected. expected.lbrace.or.semicolon='{' or ';' expected expected.parameter=Parameter expected +expected.resource=Resource definition expected expected.type.parameter=Type parameter expected expected.comma=',' expected expected.comma.or.rparen=',' or ')' expected From 91e642ac4996871bad99a6fdddf266d4aaf21124 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 18 Feb 2011 15:37:20 +0100 Subject: [PATCH 05/38] Project Coin try-with-resources support (exception handling, part 1) --- .../intellij/codeInsight/ExceptionUtil.java | 9 ++++-- .../daemon/impl/analysis/HighlightUtil.java | 22 ++++++++----- .../advHighlighting7/TryWithResources.java | 31 +++++++++++++++++++ .../daemon/LightAdvHighlightingJdk7Test.java | 6 +++- 4 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java diff --git a/java/java-impl/src/com/intellij/codeInsight/ExceptionUtil.java b/java/java-impl/src/com/intellij/codeInsight/ExceptionUtil.java index 8b95a5fd982c..c7f3c32476d5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExceptionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExceptionUtil.java @@ -338,9 +338,9 @@ public class ExceptionUtil { @NotNull private static List getUnhandledExceptions(PsiMethod method, - PsiElement element, - PsiElement topElement, - PsiSubstitutor substitutor) { + PsiElement element, + PsiElement topElement, + PsiSubstitutor substitutor) { if (method == null || isArrayClone(method, element)) { return Collections.emptyList(); } @@ -445,6 +445,9 @@ public class ExceptionUtil { if (tryStatement.getTryBlock() == element && isCaught(tryStatement, exceptionType)) { return true; } + if (tryStatement.getResourceList() == element && isCaught(tryStatement, exceptionType)) { + return true; + } PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); if (element instanceof PsiCatchSection && finallyBlock != null && blockCompletesAbruptly(finallyBlock)) { // exception swallowed diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 0dc9884a9207..3d26c4e340fc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -13,14 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * Created by IntelliJ IDEA. - * User: cdr - * Date: Jul 30, 2002 - */ package com.intellij.codeInsight.daemon.impl.analysis; +import com.google.common.collect.Sets; import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.codeInsight.daemon.impl.HighlightInfo; @@ -60,6 +55,10 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +/** + * @author cdr + * Date: Jul 30, 2002 + */ public class HighlightUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil"); private static final Map> ourInterfaceIncompatibleModifiers; @@ -857,10 +856,19 @@ public class HighlightUtil { final PsiElement declarationScope = parameter.getDeclarationScope(); if (!(declarationScope instanceof PsiCatchSection)) return null; + final Set thrownTypes = Sets.newHashSet(); + final PsiTryStatement statement = ((PsiCatchSection)declarationScope).getTryStatement(); final PsiCodeBlock tryBlock = statement.getTryBlock(); assert tryBlock != null : statement; - final Collection thrownTypes = ExceptionUtil.collectUnhandledExceptions(tryBlock, tryBlock); + thrownTypes.addAll(ExceptionUtil.collectUnhandledExceptions(tryBlock, tryBlock)); + + final PsiParameterList resources = statement.getResourceList(); + if (resources != null) { + thrownTypes.addAll(ExceptionUtil.collectUnhandledExceptions(resources, resources)); + } + + // todo: add exceptions from resource's close() method final PsiType caughtType = parameter.getType(); if (caughtType instanceof PsiClassType) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java new file mode 100644 index 000000000000..750e9bc5cb0e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java @@ -0,0 +1,31 @@ +import java.io.*; +import java.lang.Exception; + +class C { + void m0() throws Exception { + try (FileReader reader = new FileReader(new File("input.txt"))) { + reader.read(); + } catch (Exception e) { + reader = null; + } + reader = null; + } + + void m1() { + try (final FileReader reader = new FileReader(new File("input.txt"))) { + reader.read(); + } + catch (IOException ignore) { } + + try (final FileReader reader = new FileReader(new File("input.txt"))) { + System.out.println("Try."); + } + catch (IOException ignore) { } + } + + /*void m2() throws IOException { + try (final FileReader reader = new FileReader(new File("input.txt"))) { + reader.read(); + } + }*/ +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java index dcb332113c87..8ce6d97e1c8d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java @@ -153,7 +153,11 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase { public void testMultiCatch() throws Exception { doTest(false, false); } - + + public void testTryWithResources() throws Exception { + doTest(false, false); + } + public void testSafeVarargsApplicability() throws Exception { doTest(true, false); } From 1b198a33337c00626927aa42062c9263b06bdb7f Mon Sep 17 00:00:00 2001 From: Denis Zhdanov Date: Fri, 18 Feb 2011 16:31:05 +0300 Subject: [PATCH 06/38] Don't perform 'sequential formatting' if dummy special text is inserted to the document --- .../codeInsight/template/ExpressionUtil.java | 2 + .../psi/codeStyle/CodeStyleManager.java | 16 +++++ .../source/codeStyle/CodeFormatterFacade.java | 5 +- .../codeStyle/CodeStyleManagerImpl.java | 62 +++++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/template/ExpressionUtil.java b/java/java-impl/src/com/intellij/codeInsight/template/ExpressionUtil.java index de5d85de8356..f02d511da866 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/ExpressionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/ExpressionUtil.java @@ -23,6 +23,7 @@ package com.intellij.codeInsight.template; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.SuggestedNameInfo; import com.intellij.psi.codeStyle.VariableKind; + import com.intellij.psi.impl.source.codeStyle.CodeStyleManagerImpl; import com.intellij.psi.text.BlockSupport; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.Nullable; @@ -54,6 +55,7 @@ package com.intellij.codeInsight.template; ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { BlockSupport blockSupport = ServiceManager.getService(project, BlockSupport.class); + CodeStyleManagerImpl.setSequentialProcessingAllowed(false); try{ blockSupport.reparseRange(fileCopy, offset, offset, "xxx"); } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java index 4c0055ceb905..e79ff8eedfc3 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java @@ -216,4 +216,20 @@ public abstract class CodeStyleManager { * the file is read-only). */ public abstract void reformatNewlyAddedElement(@NotNull final ASTNode block, @NotNull final ASTNode addedElement) throws IncorrectOperationException; + + /** + * Formatting may be executed sequentially, i.e. the whole (re)formatting task is split into a number of smaller sub-tasks + * that are executed sequentially. That is done primarily for ability to show progress dialog during formatting (formatting + * is always performed from EDT, hence, the GUI freezes if we perform formatting as a single big iteration). + *

+ * However, there are situation when we don't want to use such an approach - for example, IntelliJ IDEA sometimes inserts dummy + * text into file in order to calculate formatting-specific data and removes it after that. We don't want to allow Swing events + * dispatching during that in order to not show that dummy text to the end-user. + *

+ * It's possible to configure that (implementation details are insignificant here) and current method serves as a read-only + * facade for obtaining information if 'sequential' processing is allowed at the moment. + * + * @return true if 'sequential' formatting is allowed now; false otherwise + */ + public abstract boolean isSequentialProcessingAllowed(); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java index 4708ab4c5307..b765cb3f1a90 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java @@ -36,6 +36,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.formatter.DocumentBasedFormattingModel; import com.intellij.psi.impl.source.PostprocessReformattingAspect; @@ -188,7 +189,9 @@ public class CodeFormatterFacade { project, mySettings, file.getFileType(), file); FormatterEx formatter = FormatterEx.getInstanceEx(); - formatter.setProgressIndicator(new FormattingProgressIndicatorImpl(project, file, document)); + if (CodeStyleManager.getInstance(project).isSequentialProcessingAllowed()) { + formatter.setProgressIndicator(new FormattingProgressIndicatorImpl(project, file, document)); + } formatter.format(model, mySettings, mySettings.getIndentOptions(file.getFileType()), ranges); for (FormatTextRanges.FormatTextRange range : textRanges) { TextRange textRange = range.getTextRange(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java index a8ab933f8d24..613654cd87fb 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java @@ -48,8 +48,20 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.concurrent.TimeUnit; + public class CodeStyleManagerImpl extends CodeStyleManager { + private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.codeStyle.CodeStyleManagerImpl"); + private static final ThreadLocal SEQUENTIAL_PROCESSING_ALLOWED + = new ThreadLocal() + { + @Override + protected ProcessingUnderProgressInfo initialValue() { + return new ProcessingUnderProgressInfo(); + } + }; + private final Project myProject; @NonNls private static final String DUMMY_IDENTIFIER = "xxx"; @@ -478,6 +490,7 @@ public class CodeStyleManagerImpl extends CodeStyleManager { document.deleteString(start, end); document.insertString(start, buffer); + setSequentialProcessingAllowed(false); document.insertString(offset, DUMMY_IDENTIFIER); return new TextRange(offset, offset + DUMMY_IDENTIFIER.length()); } @@ -506,6 +519,7 @@ public class CodeStyleManagerImpl extends CodeStyleManager { ASTNode space1 = splitSpaceElement((TreeElement)element, offset - elementStart, charTable); ASTNode marker = Factory.createSingleLeafElement(TokenType.NEW_LINE_INDENT, DUMMY_IDENTIFIER, charTable, file.getManager()); + setSequentialProcessingAllowed(false); parent.addChild(marker, space1.getTreeNext()); PsiElement psiElement = SourceTreeToPsiMap.treeElementToPsi(marker); return psiElement == null ? null : psiElement.getTextRange(); @@ -561,4 +575,52 @@ public class CodeStyleManagerImpl extends CodeStyleManager { private CodeStyleSettings getSettings() { return CodeStyleSettingsManager.getSettings(myProject); } + + @Override + public boolean isSequentialProcessingAllowed() { + return SEQUENTIAL_PROCESSING_ALLOWED.get().isAllowed(); + } + + /** + * Allows to define if {@link #isSequentialProcessingAllowed() sequential processing} should be allowed. + *

+ * Current approach is not allow to stop sequential processing for more than predefine amount of time (couple of seconds). + * That means that call to this method with 'true' argument is not mandatory for successful processing even + * if this method is called with 'false' argument before. + * + * @param allowed flag that defines if {@link #isSequentialProcessingAllowed() sequential processing} should be allowed + */ + public static void setSequentialProcessingAllowed(boolean allowed) { + ProcessingUnderProgressInfo info = SEQUENTIAL_PROCESSING_ALLOWED.get(); + if (allowed) { + info.decrement(); + } + else { + info.increment(); + } + } + + private static class ProcessingUnderProgressInfo { + + private static final long DURATION_TIME = TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS); + + private int myCount; + private long myEndTime; + + public void increment() { + myCount++; + myEndTime = System.currentTimeMillis() + DURATION_TIME; + } + + public void decrement() { + if (myCount <= 0) { + return; + } + myCount--; + } + + public boolean isAllowed() { + return myCount <= 0 || System.currentTimeMillis() >= myEndTime; + } + } } From 2366d5d403940373605f41818033fde22d5e65ec Mon Sep 17 00:00:00 2001 From: Denis Zhdanov Date: Fri, 18 Feb 2011 16:59:38 +0300 Subject: [PATCH 07/38] Revert "Don't perform 'sequential formatting' if dummy special text is inserted to the document" This reverts commit b1bb2c24cac78f80f265cb4593227ff628f89b43. Reason: choosing don't format sequentially for all actions over than 'format' --- .../codeInsight/template/ExpressionUtil.java | 2 - .../psi/codeStyle/CodeStyleManager.java | 16 ----- .../source/codeStyle/CodeFormatterFacade.java | 5 +- .../codeStyle/CodeStyleManagerImpl.java | 62 ------------------- 4 files changed, 1 insertion(+), 84 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/template/ExpressionUtil.java b/java/java-impl/src/com/intellij/codeInsight/template/ExpressionUtil.java index f02d511da866..de5d85de8356 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/ExpressionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/ExpressionUtil.java @@ -23,7 +23,6 @@ package com.intellij.codeInsight.template; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.SuggestedNameInfo; import com.intellij.psi.codeStyle.VariableKind; - import com.intellij.psi.impl.source.codeStyle.CodeStyleManagerImpl; import com.intellij.psi.text.BlockSupport; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.Nullable; @@ -55,7 +54,6 @@ package com.intellij.codeInsight.template; ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { BlockSupport blockSupport = ServiceManager.getService(project, BlockSupport.class); - CodeStyleManagerImpl.setSequentialProcessingAllowed(false); try{ blockSupport.reparseRange(fileCopy, offset, offset, "xxx"); } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java index e79ff8eedfc3..4c0055ceb905 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java @@ -216,20 +216,4 @@ public abstract class CodeStyleManager { * the file is read-only). */ public abstract void reformatNewlyAddedElement(@NotNull final ASTNode block, @NotNull final ASTNode addedElement) throws IncorrectOperationException; - - /** - * Formatting may be executed sequentially, i.e. the whole (re)formatting task is split into a number of smaller sub-tasks - * that are executed sequentially. That is done primarily for ability to show progress dialog during formatting (formatting - * is always performed from EDT, hence, the GUI freezes if we perform formatting as a single big iteration). - *

- * However, there are situation when we don't want to use such an approach - for example, IntelliJ IDEA sometimes inserts dummy - * text into file in order to calculate formatting-specific data and removes it after that. We don't want to allow Swing events - * dispatching during that in order to not show that dummy text to the end-user. - *

- * It's possible to configure that (implementation details are insignificant here) and current method serves as a read-only - * facade for obtaining information if 'sequential' processing is allowed at the moment. - * - * @return true if 'sequential' formatting is allowed now; false otherwise - */ - public abstract boolean isSequentialProcessingAllowed(); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java index b765cb3f1a90..4708ab4c5307 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java @@ -36,7 +36,6 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.formatter.DocumentBasedFormattingModel; import com.intellij.psi.impl.source.PostprocessReformattingAspect; @@ -189,9 +188,7 @@ public class CodeFormatterFacade { project, mySettings, file.getFileType(), file); FormatterEx formatter = FormatterEx.getInstanceEx(); - if (CodeStyleManager.getInstance(project).isSequentialProcessingAllowed()) { - formatter.setProgressIndicator(new FormattingProgressIndicatorImpl(project, file, document)); - } + formatter.setProgressIndicator(new FormattingProgressIndicatorImpl(project, file, document)); formatter.format(model, mySettings, mySettings.getIndentOptions(file.getFileType()), ranges); for (FormatTextRanges.FormatTextRange range : textRanges) { TextRange textRange = range.getTextRange(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java index 613654cd87fb..a8ab933f8d24 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java @@ -48,20 +48,8 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.concurrent.TimeUnit; - public class CodeStyleManagerImpl extends CodeStyleManager { - private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.codeStyle.CodeStyleManagerImpl"); - private static final ThreadLocal SEQUENTIAL_PROCESSING_ALLOWED - = new ThreadLocal() - { - @Override - protected ProcessingUnderProgressInfo initialValue() { - return new ProcessingUnderProgressInfo(); - } - }; - private final Project myProject; @NonNls private static final String DUMMY_IDENTIFIER = "xxx"; @@ -490,7 +478,6 @@ public class CodeStyleManagerImpl extends CodeStyleManager { document.deleteString(start, end); document.insertString(start, buffer); - setSequentialProcessingAllowed(false); document.insertString(offset, DUMMY_IDENTIFIER); return new TextRange(offset, offset + DUMMY_IDENTIFIER.length()); } @@ -519,7 +506,6 @@ public class CodeStyleManagerImpl extends CodeStyleManager { ASTNode space1 = splitSpaceElement((TreeElement)element, offset - elementStart, charTable); ASTNode marker = Factory.createSingleLeafElement(TokenType.NEW_LINE_INDENT, DUMMY_IDENTIFIER, charTable, file.getManager()); - setSequentialProcessingAllowed(false); parent.addChild(marker, space1.getTreeNext()); PsiElement psiElement = SourceTreeToPsiMap.treeElementToPsi(marker); return psiElement == null ? null : psiElement.getTextRange(); @@ -575,52 +561,4 @@ public class CodeStyleManagerImpl extends CodeStyleManager { private CodeStyleSettings getSettings() { return CodeStyleSettingsManager.getSettings(myProject); } - - @Override - public boolean isSequentialProcessingAllowed() { - return SEQUENTIAL_PROCESSING_ALLOWED.get().isAllowed(); - } - - /** - * Allows to define if {@link #isSequentialProcessingAllowed() sequential processing} should be allowed. - *

- * Current approach is not allow to stop sequential processing for more than predefine amount of time (couple of seconds). - * That means that call to this method with 'true' argument is not mandatory for successful processing even - * if this method is called with 'false' argument before. - * - * @param allowed flag that defines if {@link #isSequentialProcessingAllowed() sequential processing} should be allowed - */ - public static void setSequentialProcessingAllowed(boolean allowed) { - ProcessingUnderProgressInfo info = SEQUENTIAL_PROCESSING_ALLOWED.get(); - if (allowed) { - info.decrement(); - } - else { - info.increment(); - } - } - - private static class ProcessingUnderProgressInfo { - - private static final long DURATION_TIME = TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS); - - private int myCount; - private long myEndTime; - - public void increment() { - myCount++; - myEndTime = System.currentTimeMillis() + DURATION_TIME; - } - - public void decrement() { - if (myCount <= 0) { - return; - } - myCount--; - } - - public boolean isAllowed() { - return myCount <= 0 || System.currentTimeMillis() >= myEndTime; - } - } } From b4ec1e27d282ee57573f9018abd4a236421d5bc1 Mon Sep 17 00:00:00 2001 From: Denis Zhdanov Date: Fri, 18 Feb 2011 17:38:31 +0300 Subject: [PATCH 08/38] Don't perform 'sequential formatting' for all actions over 'reformat' --- .../actions/ReformatAndOptimizeImportsProcessor.java | 2 +- .../codeInsight/actions/ReformatCodeProcessor.java | 4 +++- .../src/com/intellij/formatting/FormatterImpl.java | 5 +++++ .../formatting/FormattingProgressIndicatorImpl.java | 2 ++ .../src/com/intellij/psi/formatter/FormatterUtil.java | 11 +++++++++++ .../com/intellij/openapi/actionSystem/IdeActions.java | 1 + .../com/intellij/util/containers/ContainerUtil.java | 3 ++- 7 files changed, 25 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatAndOptimizeImportsProcessor.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatAndOptimizeImportsProcessor.java index 085cb6e2aeb4..8d82a14c80be 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatAndOptimizeImportsProcessor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatAndOptimizeImportsProcessor.java @@ -28,8 +28,8 @@ import org.jetbrains.annotations.NotNull; * @author max */ public class ReformatAndOptimizeImportsProcessor extends AbstractLayoutCodeProcessor { + public static final String COMMAND_NAME = CodeInsightBundle.message("progress.reformat.code.prepare"); private static final String PROGRESS_TEXT = CodeInsightBundle.message("reformat.progress.common.text"); - private static final String COMMAND_NAME = CodeInsightBundle.message("progress.reformat.code.prepare"); private final OptimizeImportsProcessor myOptimizeImportsProcessor; private final ReformatCodeProcessor myReformatCodeProcessor; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeProcessor.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeProcessor.java index 32c786dad77a..ab3942f98bce 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeProcessor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeProcessor.java @@ -28,11 +28,13 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; public class ReformatCodeProcessor extends AbstractLayoutCodeProcessor { + + public static final String COMMAND_NAME = CodeInsightBundle.message("process.reformat.code"); + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.actions.ReformatCodeProcessor"); private final TextRange myRange; private static final String PROGRESS_TEXT = CodeInsightBundle.message("reformat.progress.common.text"); - private static final String COMMAND_NAME = CodeInsightBundle.message("process.reformat.code"); public ReformatCodeProcessor(Project project) { super(project, COMMAND_NAME, PROGRESS_TEXT); diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java index 02a7cc6ff350..0463c393a3d8 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java @@ -18,6 +18,7 @@ package com.intellij.formatting; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileType; @@ -25,6 +26,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.formatter.FormatterUtil; import com.intellij.psi.formatter.FormattingDocumentModelImpl; import com.intellij.psi.formatter.PsiBasedFormattingModel; import com.intellij.util.IncorrectOperationException; @@ -92,6 +94,9 @@ public class FormatterImpl extends FormatterEx @Override public void setProgressIndicator(@NotNull FormattingProgressIndicatorImpl progressIndicator) { + if (!FormatterUtil.FORMATTER_ACTION_NAMES.contains(CommandProcessor.getInstance().getCurrentCommandName())) { + return; + } myProgressIndicator = progressIndicator; } diff --git a/platform/lang-impl/src/com/intellij/formatting/FormattingProgressIndicatorImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormattingProgressIndicatorImpl.java index 1a57f2c4a1bc..6353ecdbb8c5 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormattingProgressIndicatorImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormattingProgressIndicatorImpl.java @@ -17,6 +17,7 @@ package com.intellij.formatting; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.ide.IdeBundle; +import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.undo.UndoManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; @@ -27,6 +28,7 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; +import com.intellij.psi.formatter.FormatterUtil; import com.intellij.util.SequentialTask; import com.intellij.util.containers.HashSet; import com.intellij.util.ui.UIUtil; diff --git a/platform/lang-impl/src/com/intellij/psi/formatter/FormatterUtil.java b/platform/lang-impl/src/com/intellij/psi/formatter/FormatterUtil.java index 15dce65f488b..a02f05eebb9d 100644 --- a/platform/lang-impl/src/com/intellij/psi/formatter/FormatterUtil.java +++ b/platform/lang-impl/src/com/intellij/psi/formatter/FormatterUtil.java @@ -15,6 +15,8 @@ */ package com.intellij.psi.formatter; +import com.intellij.codeInsight.actions.ReformatAndOptimizeImportsProcessor; +import com.intellij.codeInsight.actions.ReformatCodeProcessor; import com.intellij.lang.ASTFactory; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; @@ -27,12 +29,21 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.util.CharTable; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Set; public class FormatterUtil { + private static final List ourHelpers = ContainerUtil.createEmptyCOWList(); + + public static final Collection FORMATTER_ACTION_NAMES = Collections.unmodifiableCollection(ContainerUtil.addAll( + new HashSet(), ReformatAndOptimizeImportsProcessor.COMMAND_NAME, ReformatCodeProcessor.COMMAND_NAME + )); private FormatterUtil() { } diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java index 084d92ab0af4..919717957f94 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java @@ -51,6 +51,7 @@ public interface IdeActions { @NonNls String ACTION_EDITOR_NEXT_TEMPLATE_VARIABLE = "NextTemplateVariable"; @NonNls String ACTION_EDITOR_PREVIOUS_TEMPLATE_VARIABLE = "PreviousTemplateVariable"; + @NonNls String ACTION_EDITOR_REFORMAT = "ReformatCode"; @NonNls String ACTION_COMMENT_LINE = "CommentByLineComment"; diff --git a/platform/util/src/com/intellij/util/containers/ContainerUtil.java b/platform/util/src/com/intellij/util/containers/ContainerUtil.java index b6d593ab8f59..3aee063622d7 100644 --- a/platform/util/src/com/intellij/util/containers/ContainerUtil.java +++ b/platform/util/src/com/intellij/util/containers/ContainerUtil.java @@ -478,11 +478,12 @@ public class ContainerUtil { } } - public static void addAll(@NotNull Collection collection, @NotNull T... elements) { + public static Collection addAll(@NotNull Collection collection, @NotNull T... elements) { //noinspection ManualArrayToCollectionCopy for (T element : elements) { collection.add(element); } + return collection; } public static U findInstance(@NotNull Iterable iterable, @NotNull Class aClass) { From ed9f81b4a978db0fa618bb0cb7bfbea9288b8c66 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 18 Feb 2011 17:11:45 +0300 Subject: [PATCH 09/38] IDEA-65218 Correct background fetch Update information that current branch should be updated, after background fetch completes. --- .../src/git4idea/checkin/GitPushActiveBranchesDialog.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/git4idea/src/git4idea/checkin/GitPushActiveBranchesDialog.java b/plugins/git4idea/src/git4idea/checkin/GitPushActiveBranchesDialog.java index 371724c63a4c..a6c573d3ea83 100644 --- a/plugins/git4idea/src/git4idea/checkin/GitPushActiveBranchesDialog.java +++ b/plugins/git4idea/src/git4idea/checkin/GitPushActiveBranchesDialog.java @@ -528,14 +528,12 @@ public class GitPushActiveBranchesDialog extends DialogWrapper { * the difference is that commit checkboxes should be checked only during the initial refresh - others should respect * user who could uncheck some commits. */ - private void refreshTree(final boolean fetchData, final Map> unchecked, final boolean updateTree) { + private void refreshTree(final boolean fetchData, final Map> unchecked, final boolean updateUncheckedCommits) { myCommitTree.setPaintBusy(true); loadRootsInBackground(fetchData, new PushActiveBranchRunnable(){ @Override void run(List roots) { - if (updateTree) { - updateTree(roots, unchecked); - } + updateTree(roots, updateUncheckedCommits ? unchecked : null); updateUI(); myCommitTree.setPaintBusy(false); } From bd3519f71f2962aa64dd12c7b903e760d6fd5da4 Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Thu, 17 Feb 2011 21:12:48 +0300 Subject: [PATCH 10/38] handle horizontal alignment correctly & painting fix --- .../intellij/util/ui/ThreeStateCheckBox.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/platform/util/src/com/intellij/util/ui/ThreeStateCheckBox.java b/platform/util/src/com/intellij/util/ui/ThreeStateCheckBox.java index 16610cc2c118..4106848ee4ce 100644 --- a/platform/util/src/com/intellij/util/ui/ThreeStateCheckBox.java +++ b/platform/util/src/com/intellij/util/ui/ThreeStateCheckBox.java @@ -108,14 +108,12 @@ public class ThreeStateCheckBox extends JCheckBox { return myState; } + @Override - public void paint(final Graphics g) { - super.paint(g); + protected void paintComponent(Graphics g) { + super.paintComponent(g); switch (getState()) { case DONT_CARE: - final Rectangle r = getBounds(); - final Insets i = getInsets(); - Icon icon = getIcon(); if (icon == null) { icon = UIManager.getIcon("CheckBox.icon"); @@ -126,13 +124,24 @@ public class ThreeStateCheckBox extends JCheckBox { //if (selected != null) { // g.setColor(selected); //} + final Insets i = getInsets(); + final Rectangle r = getBounds(); + final Rectangle r1 = new Rectangle(); + r1.x = i.left; + r1.y = i.top; + r1.width = r.width - (i.right + r1.x); + r1.height = r.height - (i.bottom + r1.y); - final int width1 = icon.getIconWidth(); - final int height1 = r.height - i.top - i.bottom; - final int yoffset = height1 / 2 - 1; - final int xoffset = width1 / 2 - width1 / 5; + final Rectangle r2 = new Rectangle(); + final Rectangle r3 = new Rectangle(); + SwingUtilities.layoutCompoundLabel( + this, getFontMetrics(getFont()), getText(), icon, + getVerticalAlignment(), getHorizontalAlignment(), + getVerticalTextPosition(), getHorizontalTextPosition(), + r1, r2, r3, + getText() == null ? 0 : getIconTextGap()); - g.fillRect(xoffset + i.left, yoffset + i.top, width1 / 3, 2); + g.fillRect(r2.x + r2.width / 2 - r2.width / 5, r2.y + r2.height / 2 -1, r2.width / 3, 2); } break; default: From 6c8eb8b086c215bc9099415cfa94fa3c9d51fc36 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 18 Feb 2011 17:06:52 +0100 Subject: [PATCH 11/38] fix some problem converting if to switch in combination with existing break statements and cleanup --- .../src/com/siyeh/ipp/psiutils/BoolUtils.java | 17 ++- .../siyeh/ipp/psiutils/ControlFlowUtils.java | 56 +++++-- .../com/siyeh/ipp/switchtoif/CaseUtil.java | 142 ++++++------------ .../ipp/switchtoif/IfToSwitchPredicate.java | 4 +- .../ReplaceIfWithSwitchIntention.java | 117 +++++++-------- .../ReplaceSwitchWithIfIntention.java | 5 +- 6 files changed, 161 insertions(+), 180 deletions(-) diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/BoolUtils.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/BoolUtils.java index f769ffba04b9..80b7e93b1dc6 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/BoolUtils.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/BoolUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,14 +78,15 @@ public class BoolUtils{ return ParenthesesUtils.stripParentheses(operand); } - public static boolean isBooleanLiteral(PsiExpression exp){ - if(exp instanceof PsiLiteralExpression){ - final PsiLiteralExpression expression = (PsiLiteralExpression) exp; - @NonNls final String text = expression.getText(); - return PsiKeyword.TRUE.equals(text) || - PsiKeyword.FALSE.equals(text); + public static boolean isBooleanLiteral(PsiExpression expression){ + if (!(expression instanceof PsiLiteralExpression)) { + return false; } - return false; + final PsiLiteralExpression literalExpression = + (PsiLiteralExpression) expression; + @NonNls final String text = literalExpression.getText(); + return PsiKeyword.TRUE.equals(text) || + PsiKeyword.FALSE.equals(text); } public static String getNegatedExpressionText( diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ControlFlowUtils.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ControlFlowUtils.java index 8edf61605e13..a2daeea7182a 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ControlFlowUtils.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ControlFlowUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2005 Dave Griffith + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,12 @@ */ package com.siyeh.ipp.psiutils; +import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.psi.util.ConstantExpressionUtil; -import com.intellij.psi.util.PsiUtil; public class ControlFlowUtils{ - private ControlFlowUtils(){ - super(); - } + private ControlFlowUtils(){} public static boolean statementMayCompleteNormally(PsiStatement statement){ if(statement instanceof PsiBreakStatement || @@ -40,8 +37,10 @@ public class ControlFlowUtils{ } else if(statement instanceof PsiForStatement){ final PsiForStatement loopStatement = (PsiForStatement) statement; final PsiExpression test = loopStatement.getCondition(); - return test != null && !isBooleanConstant(test, false) || + return test != null && !isBooleanConstant(test, true) || statementIsBreakTarget(loopStatement); + } else if (statement instanceof PsiForeachStatement) { + return true; } else if(statement instanceof PsiWhileStatement){ final PsiWhileStatement loopStatement = (PsiWhileStatement) statement; @@ -144,14 +143,23 @@ public class ControlFlowUtils{ return true; } - private static boolean isBooleanConstant(PsiExpression test, boolean value){ - if(!PsiUtil.isConstantExpression(test)){ + private static boolean isBooleanConstant(PsiExpression expression, + boolean b){ + if (expression == null) { return false; } - final Boolean constantValue = - (Boolean) ConstantExpressionUtil.computeCastTo(test, - PsiType.BOOLEAN); - return constantValue != null && constantValue.booleanValue() == value; + final Project project = expression.getProject(); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiConstantEvaluationHelper constantEvaluationHelper = + psiFacade.getConstantEvaluationHelper(); + final Object value = + constantEvaluationHelper.computeConstantExpression + (expression, false); + if (!(value instanceof Boolean)) { + return false; + } + final Boolean aBoolean = (Boolean) value; + return aBoolean.booleanValue() == b; } private static boolean statementIsBreakTarget(PsiStatement statement){ @@ -178,7 +186,6 @@ public class ControlFlowUtils{ private final PsiStatement m_target; private BreakTargetFinder(PsiStatement target){ - super(); m_target = target; } @@ -186,6 +193,14 @@ public class ControlFlowUtils{ return m_found; } + @Override + public void visitElement(PsiElement element) { + if (m_found) { + return; + } + super.visitElement(element); + } + @Override public void visitReferenceExpression( PsiReferenceExpression expression){ } @@ -212,6 +227,14 @@ public class ControlFlowUtils{ return m_found; } + @Override + public void visitElement(PsiElement element) { + if (m_found) { + return; + } + super.visitElement(element); + } + @Override public void visitReferenceExpression(PsiReferenceExpression expression){ } @@ -230,6 +253,11 @@ public class ControlFlowUtils{ // don't drill down } + @Override + public void visitForeachStatement(PsiForeachStatement statement) { + // don't drill down + } + @Override public void visitWhileStatement(PsiWhileStatement statement){ // don't drill down } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/CaseUtil.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/CaseUtil.java index 54e748877077..0e1131dcfb46 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/CaseUtil.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/CaseUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package com.siyeh.ipp.switchtoif; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.siyeh.ipp.psiutils.EquivalenceChecker; import com.siyeh.ipp.psiutils.SideEffectChecker; @@ -32,63 +33,25 @@ class CaseUtil{ super(); } - private static boolean canBeCaseLabel(PsiExpression expression){ + private static boolean canBeCaseLabel(PsiExpression expression, + LanguageLevel languageLevel){ if(expression == null){ return false; } - if(expression instanceof PsiReferenceExpression){ + if (languageLevel.compareTo(LanguageLevel.JDK_1_5) >= 0 + && expression instanceof PsiReferenceExpression){ final PsiElement referent = ((PsiReference) expression).resolve(); if(referent instanceof PsiEnumConstant){ return true; } } final PsiType type = expression.getType(); - if(type == null){ - return false; - } - if(!type.equals(PsiType.INT) && - !type.equals(PsiType.CHAR) && - !type.equals(PsiType.LONG) && - !type.equals(PsiType.SHORT)){ - return false; - } - return PsiUtil.isConstantExpression(expression); - } - - public static boolean containsHiddenBreak(PsiStatement statement){ - return containsHiddenBreak(statement, true); - } - - private static boolean containsHiddenBreak(PsiStatement statement, - boolean isTopLevel){ - if(statement instanceof PsiBlockStatement){ - final PsiCodeBlock codeBlock = - ((PsiBlockStatement) statement).getCodeBlock(); - final PsiStatement[] statements = codeBlock.getStatements(); - for(final PsiStatement childStatement : statements){ - if(containsHiddenBreak(childStatement, false)){ - return true; - } - } - } else if(statement instanceof PsiIfStatement){ - final PsiIfStatement ifStatement = (PsiIfStatement) statement; - final PsiStatement thenBranch = ifStatement.getThenBranch(); - final PsiStatement elseBranch = ifStatement.getElseBranch(); - return containsHiddenBreak(thenBranch, false) || - containsHiddenBreak(elseBranch, false); - } else if(statement instanceof PsiBreakStatement){ - if(isTopLevel){ - return false; - } - final PsiIdentifier identifier = - ((PsiBreakStatement) statement).getLabelIdentifier(); - if(identifier == null){ - return true; - } - final String text = identifier.getText(); - return "".equals(text); - } - return false; + return type != null && + (type.equals(PsiType.INT) || + type.equals(PsiType.CHAR) || + type.equals(PsiType.LONG) || + type.equals(PsiType.SHORT)) && + PsiUtil.isConstantExpression(expression); } public static boolean isUsedByStatementList(PsiLocalVariable variable, @@ -109,17 +72,10 @@ class CaseUtil{ return visitor.isUsed(); } - public static String findUniqueLabel(PsiStatement statement, - @NonNls String baseName){ - PsiElement ancestor = statement; - while(ancestor.getParent() != null){ - if(ancestor instanceof PsiMethod - || ancestor instanceof PsiClass - || ancestor instanceof PsiFile){ - break; - } - ancestor = ancestor.getParent(); - } + public static String findUniqueLabelName(PsiStatement statement, + @NonNls String baseName){ + final PsiElement ancestor = + PsiTreeUtil.getParentOfType(statement, PsiMember.class); if(!checkForLabel(baseName, ancestor)){ return baseName; } @@ -140,37 +96,35 @@ class CaseUtil{ } @Nullable - public static PsiExpression getCaseExpression(PsiIfStatement statement){ + public static PsiExpression getSwitchExpression(PsiIfStatement statement){ final PsiExpression condition = statement.getCondition(); final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(statement); - final boolean stringSwitch = - languageLevel.compareTo(LanguageLevel.JDK_1_7) >= 0; - final PsiExpression possibleCaseExpression = - determinePossibleCaseExpressions(condition, stringSwitch); - if(possibleCaseExpression == null){ + final PsiExpression possibleSwitchExpression = + determinePossibleSwitchExpressions(condition, languageLevel); + if(possibleSwitchExpression == null){ return null; } - if (SideEffectChecker.mayHaveSideEffects(possibleCaseExpression)) { + if (SideEffectChecker.mayHaveSideEffects(possibleSwitchExpression)) { return null; } while(true){ final PsiExpression caseCondition = statement.getCondition(); - if (!canBeMadeIntoCase(caseCondition, possibleCaseExpression, - stringSwitch)) { + if (!canBeMadeIntoCase(caseCondition, possibleSwitchExpression, + languageLevel)) { break; } final PsiStatement elseBranch = statement.getElseBranch(); if(!(elseBranch instanceof PsiIfStatement)){ - return possibleCaseExpression; + return possibleSwitchExpression; } statement = (PsiIfStatement) elseBranch; } return null; } - private static PsiExpression determinePossibleCaseExpressions( - PsiExpression expression, boolean stringSwitch){ + private static PsiExpression determinePossibleSwitchExpressions( + PsiExpression expression, LanguageLevel languageLevel){ while(expression instanceof PsiParenthesizedExpression){ final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression; @@ -179,9 +133,9 @@ class CaseUtil{ if (expression == null) { return null; } - if (stringSwitch) { + if (languageLevel.compareTo(LanguageLevel.JDK_1_7) >= 0) { final PsiExpression jdk17Expression = - determinePossibleStringCaseExpression(expression); + determinePossibleStringSwitchExpression(expression); if (jdk17Expression != null) { return jdk17Expression; } @@ -196,18 +150,18 @@ class CaseUtil{ final PsiExpression lhs = binaryExpression.getLOperand(); final PsiExpression rhs = binaryExpression.getROperand(); if(operation.equals(JavaTokenType.OROR)){ - return determinePossibleCaseExpressions(lhs, stringSwitch); + return determinePossibleSwitchExpressions(lhs, languageLevel); } else if(operation.equals(JavaTokenType.EQEQ)){ - if(canBeCaseLabel(lhs)){ + if(canBeCaseLabel(lhs, languageLevel)){ return rhs; - } else if (canBeCaseLabel(rhs)){ + } else if (canBeCaseLabel(rhs, languageLevel)){ return lhs; } } return null; } - private static PsiExpression determinePossibleStringCaseExpression( + private static PsiExpression determinePossibleStringSwitchExpression( PsiExpression expression) { if (!(expression instanceof PsiMethodCallExpression)) { return null; @@ -216,7 +170,8 @@ class CaseUtil{ (PsiMethodCallExpression) expression; final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); - final String referenceName = methodExpression.getReferenceName(); + @NonNls final String referenceName = + methodExpression.getReferenceName(); if (!"equals".equals(referenceName)) { return null; } @@ -251,15 +206,15 @@ class CaseUtil{ private static boolean canBeMadeIntoCase( PsiExpression expression, PsiExpression caseExpression, - boolean stringSwitch) { + LanguageLevel languageLevel) { while(expression instanceof PsiParenthesizedExpression){ final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression; expression = parenthesizedExpression.getExpression(); } - if (stringSwitch) { + if (languageLevel.compareTo(LanguageLevel.JDK_1_7) >=0 ) { final PsiExpression stringCaseExpression = - determinePossibleStringCaseExpression(expression); + determinePossibleStringSwitchExpression(expression); if (EquivalenceChecker.expressionsAreEquivalent(caseExpression, stringCaseExpression)) { return true; @@ -275,20 +230,17 @@ class CaseUtil{ final PsiExpression lOperand = binaryExpression.getLOperand(); final PsiExpression rhs = binaryExpression.getROperand(); if(operation.equals(JavaTokenType.OROR)){ - return canBeMadeIntoCase(lOperand, caseExpression, stringSwitch) && - canBeMadeIntoCase(rhs, caseExpression, stringSwitch); + return canBeMadeIntoCase(lOperand, caseExpression, languageLevel) && + canBeMadeIntoCase(rhs, caseExpression, languageLevel); } else if(operation.equals(JavaTokenType.EQEQ)){ - if(canBeCaseLabel(lOperand) && - EquivalenceChecker.expressionsAreEquivalent(caseExpression, - rhs)){ - return true; - } else if(canBeCaseLabel(rhs) && - EquivalenceChecker.expressionsAreEquivalent(caseExpression, - lOperand)){ - return true; - } - return false; - } else{ + return (canBeCaseLabel(lOperand, languageLevel) && + EquivalenceChecker.expressionsAreEquivalent( + caseExpression, rhs)) + || + (canBeCaseLabel(rhs, languageLevel) && + EquivalenceChecker.expressionsAreEquivalent( + caseExpression, lOperand)); + } else { return false; } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfToSwitchPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfToSwitchPredicate.java index 54d148bbdbc4..74c28f0e3e81 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfToSwitchPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfToSwitchPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +40,6 @@ class IfToSwitchPredicate implements PsiElementPredicate{ if(ErrorUtil.containsError(statement)){ return false; } - return CaseUtil.getCaseExpression(statement) != null; + return CaseUtil.getSwitchExpression(statement) != null; } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java index 48577726b859..992c526173b4 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package com.siyeh.ipp.switchtoif; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; @@ -52,26 +53,27 @@ public class ReplaceIfWithSwitchIntention extends Intention { PsiStatement breakTarget = null; String labelString = ""; if (ControlFlowUtils.statementContainsExitingBreak(ifStatement)) { - // what a pain. - PsiElement ancestor = ifStatement.getParent(); - while (ancestor != null) { - if (ancestor instanceof PsiForStatement || - ancestor instanceof PsiDoWhileStatement || - ancestor instanceof PsiWhileStatement || - ancestor instanceof PsiSwitchStatement) { - breakTarget = (PsiStatement)ancestor; - break; - } - ancestor = ancestor.getParent(); - } + breakTarget = PsiTreeUtil.getParentOfType(ifStatement, + PsiLoopStatement.class, PsiSwitchStatement.class); if (breakTarget != null) { - labelString = CaseUtil.findUniqueLabel(ifStatement, "Label"); - breaksNeedRelabeled = true; + final PsiElement parent = breakTarget.getParent(); + if (parent instanceof PsiLabeledStatement) { + final PsiLabeledStatement labeledStatement = + (PsiLabeledStatement) parent; + labelString = + labeledStatement.getLabelIdentifier().getText(); + breakTarget = labeledStatement; + breaksNeedRelabeled = true; + } else { + labelString = CaseUtil.findUniqueLabelName(ifStatement, + "label"); + breaksNeedRelabeled = true; + } } } final PsiIfStatement statementToReplace = ifStatement; final PsiExpression caseExpression = - CaseUtil.getCaseExpression(ifStatement); + CaseUtil.getSwitchExpression(ifStatement); assert caseExpression != null; final List branches = @@ -181,8 +183,10 @@ public class ReplaceIfWithSwitchIntention extends Intention { final PsiElementFactory factory = psiFacade.getElementFactory(); if (breaksNeedRelabeled) { final StringBuilder out = new StringBuilder(); - out.append(labelString); - out.append(':'); + if (!(breakTarget instanceof PsiLabeledStatement)) { + out.append(labelString); + out.append(':'); + } termReplace(out, breakTarget, statementToReplace, switchStatementText); final String newStatementText = out.toString(); @@ -271,8 +275,7 @@ public class ReplaceIfWithSwitchIntention extends Intention { if (target.equals(replace)) { out.append(stringToReplaceWith); } else if (target.getChildren().length == 0) { - final String text = target.getText(); - out.append(text); + out.append(target.getText()); } else { final PsiElement[] children = target.getChildren(); for (final PsiElement child : children) { @@ -322,35 +325,36 @@ public class ReplaceIfWithSwitchIntention extends Intention { } } } else if (expression instanceof PsiParenthesizedExpression) { - final PsiParenthesizedExpression parenExpression = + final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression; - final PsiExpression contents = parenExpression.getExpression(); + final PsiExpression contents = + parenthesizedExpression.getExpression(); getValuesFromExpression(contents, caseExpression, values); } return values; } - private static void dumpBranch(StringBuilder switchStatementString, + private static void dumpBranch(StringBuilder switchStatementText, List comments, List labels, List statementComments, PsiStatement body, boolean wrap, boolean renameBreaks, String breakLabelName) { - dumpComments(switchStatementString, comments); - dumpLabels(switchStatementString, labels); - dumpComments(switchStatementString, statementComments); - dumpBody(switchStatementString, body, wrap, renameBreaks, + dumpComments(switchStatementText, comments); + dumpLabels(switchStatementText, labels); + dumpComments(switchStatementText, statementComments); + dumpBody(switchStatementText, body, wrap, renameBreaks, breakLabelName); } - private static void dumpComments(StringBuilder switchStatementString, + private static void dumpComments(StringBuilder switchStatementText, List comments) { if (!comments.isEmpty()) { - switchStatementString.append('\n'); + switchStatementText.append('\n'); for (String comment : comments) { - switchStatementString.append(comment); - switchStatementString.append('\n'); + switchStatementText.append(comment); + switchStatementText.append('\n'); } } } @@ -367,21 +371,21 @@ public class ReplaceIfWithSwitchIntention extends Intention { breakLabelName); } - private static void dumpLabels(@NonNls StringBuilder switchStatementString, + private static void dumpLabels(@NonNls StringBuilder switchStatementText, List labels) { for (String label : labels) { - switchStatementString.append("case "); - switchStatementString.append(label); - switchStatementString.append(": "); + switchStatementText.append("case "); + switchStatementText.append(label); + switchStatementText.append(": "); } } - private static void dumpBody(@NonNls StringBuilder switchStatementString, + private static void dumpBody(@NonNls StringBuilder switchStatementText, PsiStatement bodyStatement, boolean wrap, boolean renameBreaks, String breakLabelName) { if (bodyStatement instanceof PsiBlockStatement) { if (wrap) { - appendElement(switchStatementString, bodyStatement, + appendElement(switchStatementText, bodyStatement, renameBreaks, breakLabelName); } else { final PsiCodeBlock codeBlock = @@ -390,60 +394,55 @@ public class ReplaceIfWithSwitchIntention extends Intention { //skip the first and last members, to unwrap the block for (int i = 1; i < children.length - 1; i++) { final PsiElement child = children[i]; - appendElement(switchStatementString, child, renameBreaks, + appendElement(switchStatementText, child, renameBreaks, breakLabelName); } } } else { if (wrap) { - switchStatementString.append('{'); - appendElement(switchStatementString, bodyStatement, + switchStatementText.append('{'); + appendElement(switchStatementText, bodyStatement, renameBreaks, breakLabelName); - switchStatementString.append('}'); + switchStatementText.append('}'); } else { - appendElement(switchStatementString, bodyStatement, + appendElement(switchStatementText, bodyStatement, renameBreaks, breakLabelName); } } if (ControlFlowUtils.statementMayCompleteNormally(bodyStatement)) { - switchStatementString.append("break; "); + switchStatementText.append("break; "); } } private static void appendElement( - @NonNls StringBuilder switchStatementString, + @NonNls StringBuilder switchStatementText, PsiElement element, boolean renameBreakElements, String breakLabelString) { final String text = element.getText(); if (!renameBreakElements) { - switchStatementString.append(text); + switchStatementText.append(text); } else if (element instanceof PsiBreakStatement) { + final PsiBreakStatement breakStatement = + (PsiBreakStatement) element; final PsiIdentifier identifier = - ((PsiBreakStatement)element).getLabelIdentifier(); + breakStatement.getLabelIdentifier(); if (identifier == null) { - switchStatementString.append("break "); - switchStatementString.append(breakLabelString); - switchStatementString.append(';'); + switchStatementText.append("break "); + switchStatementText.append(breakLabelString); + switchStatementText.append(';'); } else { - final String identifierText = identifier.getText(); - if ("".equals(identifierText)) { - switchStatementString.append("break "); - switchStatementString.append(breakLabelString); - switchStatementString.append(';'); - } else { - switchStatementString.append(text); - } + switchStatementText.append(text); } } else if (element instanceof PsiBlockStatement || element instanceof PsiCodeBlock || element instanceof PsiIfStatement) { final PsiElement[] children = element.getChildren(); for (final PsiElement child : children) { - appendElement(switchStatementString, child, renameBreakElements, + appendElement(switchStatementText, child, renameBreakElements, breakLabelString); } } else { - switchStatementString.append(text); + switchStatementText.append(text); } } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java index a383358636d6..baf4206c6ae8 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java @@ -92,7 +92,7 @@ public class ReplaceSwitchWithIfIntention extends Intention { final PsiStatement[] statements = body.getStatements(); boolean renameBreaks = false; for (int i = 1; i < statements.length - 1; i++) { - if (CaseUtil.containsHiddenBreak(statements[i])) { + if (ControlFlowUtils.statementContainsExitingBreak(statements[i])) { renameBreaks = true; break; } @@ -163,7 +163,8 @@ public class ReplaceSwitchWithIfIntention extends Intention { final StringBuilder ifStatementText = new StringBuilder(); String breakLabel = null; if (renameBreaks) { - breakLabel = CaseUtil.findUniqueLabel(switchStatement, "Label"); + breakLabel = + CaseUtil.findUniqueLabelName(switchStatement, "label"); ifStatementText.append(breakLabel); ifStatementText.append(':'); } From c093255244d789a11da7ec32bfb0ac325ae8cdc0 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 18 Feb 2011 17:51:01 +0100 Subject: [PATCH 12/38] fix test data? --- .../com/intellij/projectView/NavigateFromSourceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java b/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java index 6c41be62867f..2e5167fbee58 100644 --- a/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java +++ b/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java @@ -72,7 +72,7 @@ public class NavigateFromSourceTest extends BaseProjectViewTestCase { " -PsiDirectory: src\n" + " -PsiDirectory: com\n" + " -PsiDirectory: package1\n" + - " -[Class1.java]\n" + + " -Class1.java\n" + " [Class11]\n" + " Class2\n" + getRootFiles() + From f726897aa4f75571384c83263b4599afa04cb600 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 18 Feb 2011 18:24:39 +0100 Subject: [PATCH 13/38] fix navigation to the class non-code usages (don't go to the start of the file) --- .../src/com/intellij/psi/SmartPsiElementPointer.java | 1 + .../lang-api/src/com/intellij/usageView/UsageInfo.java | 8 ++++++++ .../psi/impl/smartPointers/SmartPointerElementInfo.java | 2 ++ 3 files changed, 11 insertions(+) diff --git a/platform/lang-api/src/com/intellij/psi/SmartPsiElementPointer.java b/platform/lang-api/src/com/intellij/psi/SmartPsiElementPointer.java index 13aae3b5b91a..b42dacbd79f1 100644 --- a/platform/lang-api/src/com/intellij/psi/SmartPsiElementPointer.java +++ b/platform/lang-api/src/com/intellij/psi/SmartPsiElementPointer.java @@ -46,5 +46,6 @@ public interface SmartPsiElementPointer { VirtualFile getVirtualFile(); + @Nullable Segment getRange(); } diff --git a/platform/lang-api/src/com/intellij/usageView/UsageInfo.java b/platform/lang-api/src/com/intellij/usageView/UsageInfo.java index 53a29b7cebf5..425b01cc2929 100644 --- a/platform/lang-api/src/com/intellij/usageView/UsageInfo.java +++ b/platform/lang-api/src/com/intellij/usageView/UsageInfo.java @@ -136,6 +136,13 @@ public class UsageInfo { } public int getNavigationOffset() { + if (mySmartPointer instanceof SmartPsiFileRange) { + final Segment range = mySmartPointer.getRange(); + if (range != null) { + return range.getStartOffset(); + } + } + PsiElement element = getElement(); if (element == null) return -1; TextRange range = element.getTextRange(); @@ -145,6 +152,7 @@ public class UsageInfo { return range.getStartOffset() + rangeInElement.getStartOffset(); } + @Nullable public Segment getSegment() { PsiElement element = getElement(); if (element == null) return null; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java index e7676494b293..8ef474f6dd1e 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java @@ -41,6 +41,8 @@ interface SmartPointerElementInfo { boolean pointsToTheSameElementAs(SmartPointerElementInfo other); VirtualFile getVirtualFile(); + + @Nullable Segment getRange(); @NotNull Project getProject(); } From 9a8b04b383896815379d36fea76489056767175a Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 18 Feb 2011 18:30:29 +0100 Subject: [PATCH 14/38] on escape cancel the hint autopopup --- .../intellij/codeInsight/lookup/impl/LookupImpl.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index d12bd938be93..3eb3cf6fb80b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java @@ -1086,13 +1086,13 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { assert myHidden; assert !myDisposed : disposeTrace; - hideAutopopupHint(); - Disposer.dispose(myProcessIcon); Disposer.dispose(myHintAlarm); myDisposed = true; disposeTrace = DebugUtil.currentStackTrace(); + + hideAutopopupHint(); } private int doSelectMostPreferableItem(List items) { @@ -1155,7 +1155,13 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { if (myAutopopupHint == null) { final JPanel panel = new JPanel(new BorderLayout()); panel.add(hintComponent); - myAutopopupHint = new LightweightHint(panel); + myAutopopupHint = new LightweightHint(panel) { + @Override + public void hide() { + hideLookup(true); + super.hide(); + } + }; myAutopopupHint.setForceShowAsPopup(true); hintManager.showEditorHint(myAutopopupHint, editor, new Point(bestPoint), HintManagerImpl.HIDE_BY_ESCAPE | HintManagerImpl.UPDATE_BY_SCROLLING, 0, false, hintHint); From dcad2f2248f4b1c998ef26c58560c01727eb6d4a Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 18 Feb 2011 18:44:17 +0100 Subject: [PATCH 15/38] more spacing at the bottom --- .../src/com/intellij/codeInsight/lookup/impl/LookupImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index 3eb3cf6fb80b..ca3956129aee 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java @@ -1246,7 +1246,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { if (items.size() > maxAutopopupItems) { final String ctrlSpace = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_CODE_COMPLETION)); if (StringUtil.isNotEmpty(ctrlSpace)) { - final String moreText = ctrlSpace + " for more"; + final String moreText = ctrlSpace + " for more "; final JLabel moreLabel = new JLabel(moreText); moreLabel.setFont(moreLabel.getFont().deriveFont(Font.PLAIN, editorFont.getSize())); ad.add(moreLabel, BorderLayout.WEST); @@ -1256,7 +1256,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { final String tab = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM_REPLACE)); if (StringUtil.isNotEmpty(tab)) { final String enter = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM)); - String message = tab + (isFocused() ? ", " + enter : "") + " for the first item"; + String message = " " + tab + (isFocused() ? ", " + enter : "") + " for the first item"; final JLabel fstLabel = new JLabel(message); fstLabel.setFont(fstLabel.getFont().deriveFont(Font.PLAIN, editorFont.getSize())); ad.add(fstLabel, BorderLayout.EAST); From a5f56173c8dc71ae32145bff530befc755fd2a6e Mon Sep 17 00:00:00 2001 From: andrey zaytsev Date: Fri, 18 Feb 2011 20:51:34 +0300 Subject: [PATCH 16/38] search/replace refactoring. added exclude function. fixed unexpected scrolling when document changes. multiline occurrences highlithting --- .../intellij/find/EditorSearchComponent.java | 174 +++--- .../src/com/intellij/find/FindUtil.java | 24 +- .../com/intellij/find/impl/FindDialog.java | 12 +- .../com/intellij/find/impl/LivePreview.java | 570 ------------------ .../find/impl/LivePreviewController.java | 147 ----- .../find/impl/LivePreviewControllerBase.java | 150 ----- .../intellij/find/impl/ReplacementView.java | 66 -- .../{ => livePreview}/LiveOccurrence.java | 7 +- .../find/impl/livePreview/LivePreview.java | 336 +++++++++++ .../LivePreviewControllerBase.java | 119 ++++ .../impl/livePreview/ReplacementView.java | 125 ++++ .../find/impl/livePreview/SearchResults.java | 328 ++++++++++ 12 files changed, 1034 insertions(+), 1024 deletions(-) delete mode 100644 platform/lang-impl/src/com/intellij/find/impl/LivePreview.java delete mode 100644 platform/lang-impl/src/com/intellij/find/impl/LivePreviewController.java delete mode 100644 platform/lang-impl/src/com/intellij/find/impl/LivePreviewControllerBase.java delete mode 100644 platform/lang-impl/src/com/intellij/find/impl/ReplacementView.java rename platform/lang-impl/src/com/intellij/find/impl/{ => livePreview}/LiveOccurrence.java (87%) create mode 100644 platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreview.java create mode 100644 platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreviewControllerBase.java create mode 100644 platform/lang-impl/src/com/intellij/find/impl/livePreview/ReplacementView.java create mode 100644 platform/lang-impl/src/com/intellij/find/impl/livePreview/SearchResults.java diff --git a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java index ba6491c7c18b..bfd283667e94 100644 --- a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java +++ b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java @@ -21,9 +21,10 @@ package com.intellij.find; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.find.impl.FindManagerImpl; -import com.intellij.find.impl.LiveOccurrence; -import com.intellij.find.impl.LivePreview; -import com.intellij.find.impl.LivePreviewControllerBase; +import com.intellij.find.impl.livePreview.LiveOccurrence; +import com.intellij.find.impl.livePreview.LivePreview; +import com.intellij.find.impl.livePreview.LivePreviewControllerBase; +import com.intellij.find.impl.livePreview.SearchResults; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; @@ -38,7 +39,6 @@ import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.PopupChooserBuilder; import com.intellij.openapi.util.IconLoader; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; @@ -69,8 +69,8 @@ import java.util.HashSet; import java.util.Set; import java.util.regex.Pattern; -public class EditorSearchComponent extends JPanel implements DataProvider, LivePreview.CursorListener, SelectionListener { - private static final int MATCHES_LIMIT = 100; +public class EditorSearchComponent extends JPanel implements DataProvider, SelectionListener, SearchResults.SearchResultsListener { + private static final int MATCHES_LIMIT = 10000; private final JLabel myMatchInfoLabel; private final LinkLabel myClickToHighlightLabel; private final Project myProject; @@ -95,51 +95,14 @@ public class EditorSearchComponent extends JPanel implements DataProvider, LiveP private final JCheckBox myCbInComments; private final JCheckBox myCbInLiterals; - private final LivePreviewControllerBase myLivePreviewController = new LivePreviewControllerBase() { - @Override - public void getFocusBack() { - mySearchField.requestFocus(); - } - - @Override - public Editor getEditor(Ref needToUpdate) { - needToUpdate.set(true); - return myEditor; - } - - @Override - public void searchEndsWith(int count) { - if (count <= getMatchesLimit()) { - myClickToHighlightLabel.setVisible(false); - - if (count > 0) { - setRegularBackground(); - if (count > 1) { - myMatchInfoLabel.setText(count + " matches"); - } - else { - myMatchInfoLabel.setText("1 match"); - } - } - else { - setNotFoundBackground(); - myMatchInfoLabel.setText("No matches"); - } - } - else { - setRegularBackground(); - myMatchInfoLabel.setText("More than 100 matches"); - myClickToHighlightLabel.setVisible(true); - boldMatchInfo(); - } - } - }; + private final LivePreviewControllerBase myLivePreviewController; private final LivePreview myLivePreview; private boolean myIsReplace; private boolean myListeningSelection = false; private boolean myToChangeSelection = true; + private SearchResults mySearchResults; @Nullable public Object getData(@NonNls final String dataId) { @@ -153,12 +116,71 @@ public class EditorSearchComponent extends JPanel implements DataProvider, LiveP this(e, p, false); } + @Override + public void searchResultsUpdated(SearchResults sr) { + int count = sr.getActualFound(); + if (mySearchField.getText().isEmpty()) { + nothingToSearchFor(); + return; + } + + if (count <= mySearchResults.getMatchesLimit()) { + myClickToHighlightLabel.setVisible(false); + + if (count > 0) { + setRegularBackground(); + if (count > 1) { + myMatchInfoLabel.setText(count + " matches"); + } + else { + myMatchInfoLabel.setText("1 match"); + } + } + else { + setNotFoundBackground(); + myMatchInfoLabel.setText("No matches"); + } + } + else { + setRegularBackground(); + myMatchInfoLabel.setText("More than " + mySearchResults.getMatchesLimit() + " matches"); + myClickToHighlightLabel.setVisible(true); + boldMatchInfo(); + } + + updateSelection(); + } + + @Override + public void cursorMoved() { + updateSelection(); + } + + private void updateSelection() { + if (myToChangeSelection && (mySelectionOnly == null || !mySelectionOnly.isSelected())) { + LiveOccurrence cursor = mySearchResults.getCursor(); + if (cursor != null) { + TextRange range = cursor.getPrimaryRange(); + myEditor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset()); + + myEditor.getCaretModel().moveToOffset(range.getEndOffset()); + myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + + myEditor.getCaretModel().moveToOffset(range.getStartOffset()); + myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } + myToChangeSelection = false; + } + } + + @Override + public void editorChanged(SearchResults sr, Editor oldEditor) { } + public EditorSearchComponent(final Editor editor, final Project project, boolean isReplace) { super(new BorderLayout(0, 0)); myIsReplace = isReplace; - setMatchesLimit(MATCHES_LIMIT); GRADIENT_C1 = getBackground(); GRADIENT_C2 = new Color(Math.max(0, GRADIENT_C1.getRed() - 0x18), Math.max(0, GRADIENT_C1.getGreen() - 0x18), Math.max(0, GRADIENT_C1.getBlue() - 0x18)); @@ -166,9 +188,18 @@ public class EditorSearchComponent extends JPanel implements DataProvider, LiveP myProject = project; myEditor = editor; - myLivePreview = new LivePreview(myEditor.getProject(), 0); - myLivePreview.addCursorListener(this); - myLivePreview.setDelegate(myLivePreviewController); + mySearchResults = new SearchResults(myEditor); + myLivePreview = new LivePreview(mySearchResults); + + myLivePreviewController = new LivePreviewControllerBase(mySearchResults, myLivePreview) { + @Override + public void getFocusBack() { + mySearchField.requestFocus(); + } + }; + mySearchResults.addListener(this); + setMatchesLimit(MATCHES_LIMIT); + JPanel leadPanel = createLeadPane(); add(leadPanel, BorderLayout.WEST); @@ -338,7 +369,7 @@ public class EditorSearchComponent extends JPanel implements DataProvider, LiveP } private void setMatchesLimit(int value) { - myLivePreviewController.setMatchesLimit(value); + mySearchResults.setMatchesLimit(value); } private void configureReplacementPane() { @@ -483,24 +514,6 @@ public class EditorSearchComponent extends JPanel implements DataProvider, LiveP } } - @Override - public void cursorMoved() { - if (myToChangeSelection && (mySelectionOnly == null || !mySelectionOnly.isSelected())) { - LiveOccurrence cursor = myLivePreview.getCursor(); - if (cursor != null) { - TextRange range = cursor.getPrimaryRange(); - myEditor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset()); - - myEditor.getCaretModel().moveToOffset(range.getEndOffset()); - myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); - - myEditor.getCaretModel().moveToOffset(range.getStartOffset()); - myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); - } - myToChangeSelection = false; - } - } - @Override public void selectionChanged(SelectionEvent e) { updateResults(true); @@ -509,9 +522,9 @@ public class EditorSearchComponent extends JPanel implements DataProvider, LiveP public void moveCursor(boolean forwardOrBackward) { myToChangeSelection = true; if (forwardOrBackward) { - myLivePreview.nextOccurrence(); + mySearchResults.nextOccurrence(); } else { - myLivePreview.prevOccurrence(); + mySearchResults.prevOccurrence(); } } @@ -555,7 +568,7 @@ public class EditorSearchComponent extends JPanel implements DataProvider, LiveP myEditor.getDocument().addDocumentListener(myDocumentListener); if (myLivePreview != null) { - myLivePreview.update(); + myLivePreviewController.updateInBackground(mySearchResults.getFindModel()); } } @@ -576,12 +589,7 @@ public class EditorSearchComponent extends JPanel implements DataProvider, LiveP myMatchInfoLabel.setFont(myMatchInfoLabel.getFont().deriveFont(Font.PLAIN)); final String text = mySearchField.getText(); if (text.length() == 0) { - setRegularBackground(); - myMatchInfoLabel.setText(""); - myClickToHighlightLabel.setVisible(false); - if (myLivePreview != null) { - myLivePreview.cleanUp(); - } + nothingToSearchFor(); } else { final FindModel model = new FindModel(); @@ -628,9 +636,17 @@ public class EditorSearchComponent extends JPanel implements DataProvider, LiveP model.setPreserveCase(myPreserveCase.isEnabled() && myPreserveCase.isSelected()); } myToChangeSelection = allowedToChangedEditorSelection; - myLivePreviewController.setFindModel(model); - myLivePreview.update(); + myLivePreviewController.updateInBackground(model); + } + } + + private void nothingToSearchFor() { + setRegularBackground(); + myMatchInfoLabel.setText(""); + myClickToHighlightLabel.setVisible(false); + if (myLivePreview != null) { + myLivePreview.cleanUp(); } } diff --git a/platform/lang-impl/src/com/intellij/find/FindUtil.java b/platform/lang-impl/src/com/intellij/find/FindUtil.java index 1e0808379486..03d0f56d5cbe 100644 --- a/platform/lang-impl/src/com/intellij/find/FindUtil.java +++ b/platform/lang-impl/src/com/intellij/find/FindUtil.java @@ -410,6 +410,15 @@ public class FindUtil { } public static boolean replace(Project project, Editor editor, int offset, FindModel model) { + return replace(project, editor, offset, model, new ReplaceDelegate() { + @Override + public boolean shouldReplace(TextRange range, String replace) { + return true; + } + }); + } + + public static boolean replace(Project project, Editor editor, int offset, FindModel model, ReplaceDelegate delegate) { Document document = editor.getDocument(); if (!FileDocumentManager.getInstance().requestWriting(document, project)) { @@ -423,7 +432,7 @@ public class FindUtil { ((DocumentEx) document).setInBulkUpdate(true); } try { - toPrompt = doReplace(project, editor, model, document, offset, toPrompt); + toPrompt = doReplace(project, editor, model, document, offset, toPrompt, delegate); } catch (ReadOnlyFragmentModificationException e) { EditorActionManager.getInstance().getReadonlyFragmentModificationHandler(document).handle(e); @@ -438,7 +447,8 @@ public class FindUtil { return true; } - private static boolean doReplace(Project project, Editor editor, FindModel model, final Document document, int caretOffset, boolean toPrompt) { + private static boolean doReplace(Project project, Editor editor, FindModel model, final Document document, int caretOffset, + boolean toPrompt, ReplaceDelegate delegate) { FindManager findManager = FindManager.getInstance(project); model = (FindModel)model.clone(); int occurrences = 0; @@ -517,7 +527,11 @@ public class FindUtil { TextRange range = pair.getFirst(); String replace = pair.getSecond(); newText.append(text, offsetBefore, range.getStartOffset()); //before change - newText.append(replace); + if (delegate.shouldReplace(range, replace)) { + newText.append(replace); + } else { + newText.append(text.subSequence(range.getStartOffset(), range.getEndOffset())); + } offsetBefore = range.getEndOffset(); if (offsetBefore < caretOffset) { caretOffset += replace.length() - range.getLength(); @@ -761,4 +775,8 @@ public class FindUtil { editor.getCaretModel().moveToLogicalPosition(pos); editor.getScrollingModel().scrollToCaret(scrollType); } + + public interface ReplaceDelegate { + boolean shouldReplace(TextRange range, String replace) ; + } } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java index 2bcee259eec9..0f818f3e59f6 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java @@ -19,7 +19,6 @@ package com.intellij.find.impl; import com.intellij.CommonBundle; import com.intellij.find.FindBundle; -import com.intellij.find.FindManager; import com.intellij.find.FindModel; import com.intellij.find.FindSettings; import com.intellij.ide.util.scopeChooser.ScopeChooserCombo; @@ -108,9 +107,6 @@ class FindDialog extends DialogWrapper { private JRadioButton myRbCustomScope; private ScopeChooserCombo myScopeCombo; - private LivePreviewController myLivePreviewController; - - public FindDialog(Project project, FindModel model, Runnable myOkHandler){ super(project, true); myProject = project; @@ -139,7 +135,7 @@ class FindDialog extends DialogWrapper { setOKButtonIcon(IconLoader.getIcon("/actions/find.png")); init(); initByModel(); - myLivePreviewController = new LivePreviewController(this, new LivePreview(myProject), getContentPane()); + //myLivePreviewController = new LivePreviewController(this, new LivePreview(myProject), getContentPane()); } @Override @@ -148,9 +144,9 @@ class FindDialog extends DialogWrapper { e.getKey().removeDocumentListener(e.getValue()); } myComboBoxListeners.clear(); - if (myLivePreviewController != null) { - myLivePreviewController.cleanUp(); - } + //if (myLivePreviewController != null) { + // myLivePreviewController.cleanUp(); + //} super.dispose(); } diff --git a/platform/lang-impl/src/com/intellij/find/impl/LivePreview.java b/platform/lang-impl/src/com/intellij/find/impl/LivePreview.java deleted file mode 100644 index e092ac5293fe..000000000000 --- a/platform/lang-impl/src/com/intellij/find/impl/LivePreview.java +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright 2000-2011 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.find.impl; - - -import com.intellij.codeInsight.highlighting.HighlightManager; -import com.intellij.ide.IdeTooltipManager; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.ScrollType; -import com.intellij.openapi.editor.VisualPosition; -import com.intellij.openapi.editor.event.DocumentAdapter; -import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.editor.event.VisibleAreaEvent; -import com.intellij.openapi.editor.event.VisibleAreaListener; -import com.intellij.openapi.editor.markup.CustomHighlighterRenderer; -import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.ui.popup.BalloonBuilder; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.TextRange; -import com.intellij.ui.awt.RelativePoint; -import com.intellij.util.Alarm; -import com.intellij.util.containers.HashSet; -import com.intellij.util.ui.PositionTracker; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.awt.*; -import java.util.*; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -public class LivePreview extends DocumentAdapter implements ReplacementView.Delegate { - - private final Collection myHighlighters = new HashSet(); - private RangeHighlighter myCursorHighlighter; - private final List myVisibleAreaListenersToRemove = new ArrayList(); - private boolean myShouldStop; - - @Override - public void performReplacement(LiveOccurrence occurrence, String replacement) { - if (myDelegate != null) { - final TextRange textRange = myDelegate.performReplace(occurrence, replacement, myEditor); - if (textRange != null) { - updateInBackground(); - setContinuation(new Runnable() { - @Override - public void run() { - if (mySearchResults != null) { - LiveOccurrence nearest = null; - int minDist = Integer.MAX_VALUE; - for (LiveOccurrence o : mySearchResults) { - if (nearest == null) { - nearest = o; - } - int dist = Math.abs(o.getPrimaryRange().getStartOffset() - textRange.getStartOffset()); - if (dist < minDist) { - minDist = dist; - nearest = o; - } - } - if (nearest != null) { - moveCursorTo(nearest); - } - } - } - }); - myDelegate.getFocusBack(); - } - } - - } - - @Override - public void performReplaceAll() { - myDelegate.performReplaceAll(myEditor); - } - - public boolean hasMatches() { - return mySearchResults != null && !mySearchResults.isEmpty(); - } - - public LiveOccurrence getCursor() { - return myCursor; - } - - public Project getProject() { - return myProject; - } - - public Editor getEditor() { - return myEditor; - } - - public interface CursorListener { - void cursorMoved(); - } - - private final List myListeners = new ArrayList(); - - public void addCursorListener(CursorListener listener) { - myListeners.add(listener); - } - - public void removeCursorListener(CursorListener listener) { - myListeners.remove(listener); - } - - public interface Delegate { - @NotNull - List performSearchInBackgroundInReadAction(Editor editor); - - @Nullable - String getReplacementPreviewText(Editor editor, LiveOccurrence liveOccurrence); - - TextRange performReplace(LiveOccurrence occurrence, String replacement, Editor editor); - - void performReplaceAll(Editor e); - - void getFocusBack(); - - Editor getEditor(Ref needToUpdate); - } - - private static final int USER_ACTIVITY_TRIGGERING_DELAY = 300; - - private static final TextAttributes OTHER_TARGETS_ATTRIBUTES = new TextAttributes(Color.BLACK, Color.GREEN, null, null, 0); - private static final TextAttributes MAIN_TARGET_ATTRIBUTES = new TextAttributes(Color.BLACK, Color.YELLOW, null, null, 0); - - private final Alarm myLivePreviewAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); - - private final Project myProject; - private Editor myEditor; - - private Delegate myDelegate; - - private LiveOccurrence myCursor; - - private Runnable myContinuation; - - private int myUserActivityDelay = USER_ACTIVITY_TRIGGERING_DELAY; - - public Runnable getContinuation() { - return myContinuation; - } - - public void setContinuation(Runnable continuation) { - myContinuation = continuation; - } - - public List getSearchResults() { - return mySearchResults; - } - - private List mySearchResults; - - private Balloon myReplacementBalloon; - - public LivePreview(Project project) { - myProject = project; - } - - public LivePreview(Project project, int delay) { - this(project); - myUserActivityDelay = delay; - } - - public Delegate getDelegate() { - return myDelegate; - } - - public void setDelegate(Delegate delegate) { - myDelegate = delegate; - } - - @Nullable - private Editor updateEditorReference() { - if (myProject == null) return null; - Ref needToUpdate = new Ref(); - Editor result = myDelegate.getEditor(needToUpdate); - if (!needToUpdate.isNull() && needToUpdate.get()) { - setEditor(result); - } - return myEditor; - } - - private void setEditor(Editor editor) { - if (editor != myEditor) { - doInternalCleanUp(); - } - if (myEditor != null) { - myEditor.getDocument().removeDocumentListener(this); - } - myEditor = editor; - if (myEditor != null) { - myEditor.getDocument().addDocumentListener(this); - } - } - - @Override - public void documentChanged(DocumentEvent e) { - doInternalCleanUp(); - } - - public void update() { - myShouldStop = false; - myLivePreviewAlarm.cancelAllRequests(); - if (updateEditorReference() != null) { - Runnable request = new Runnable() { - @Override - public void run() { - updateInBackground(); - } - }; - if (ApplicationManager.getApplication().isUnitTestMode()) { - request.run(); - } else { - myLivePreviewAlarm.addRequest(request, myUserActivityDelay); - } - } - } - - public void cleanUp() { - myShouldStop = true; - myLivePreviewAlarm.cancelAllRequests(); - doInternalCleanUp(); - } - - private void doInternalCleanUp() { - if (myReplacementBalloon != null) { - myReplacementBalloon.hide(); - } - if (myEditor != null) { - - for (VisibleAreaListener visibleAreaListener : myVisibleAreaListenersToRemove) { - myEditor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener); - } - myVisibleAreaListenersToRemove.clear(); - for (RangeHighlighter h : myHighlighters) { - HighlightManager.getInstance(myProject).removeSegmentHighlighter(myEditor, h); - } - if (myCursorHighlighter != null) { - HighlightManager.getInstance(myProject).removeSegmentHighlighter(myEditor, myCursorHighlighter); - myCursorHighlighter = null; - } - } - } - - private void updateInBackground() { - if (myDelegate == null) return; - final TextRange oldCursorRange = myCursor != null ? myCursor.getPrimaryRange() : null; - mySearchResults = performSearchInBackground(); - Collections.sort(mySearchResults, new Comparator() { - @Override - public int compare(LiveOccurrence liveOccurrence, LiveOccurrence liveOccurence1) { - return liveOccurrence.getPrimaryRange().getStartOffset() - liveOccurence1.getPrimaryRange().getStartOffset(); - } - }); - if (mySearchResults != null) { - Runnable highlightUsagesBlock = new Runnable() { - @Override - public void run() { - doInternalCleanUp(); - highlightUsages(oldCursorRange); - if (myContinuation != null) { - myContinuation.run(); - myContinuation = null; - } - } - }; - if (ApplicationManager.getApplication().isUnitTestMode()) { - highlightUsagesBlock.run(); - } else { - ApplicationManager.getApplication().invokeLater(highlightUsagesBlock); - } - } - } - - private List performSearchInBackground() { - final AtomicReference> occurrences = new AtomicReference>(); - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - occurrences.set(myDelegate.performSearchInBackgroundInReadAction(myEditor)); - } - }); - return occurrences.get(); - } - - private void highlightUsages(TextRange oldCursorRange) { - if (myEditor == null || myShouldStop) return; - for (LiveOccurrence o : mySearchResults) { - for (TextRange textRange : o.getSecondaryRanges()) { - highlightRange(textRange, OTHER_TARGETS_ATTRIBUTES, myHighlighters); - } - highlightRange(o.getPrimaryRange(), MAIN_TARGET_ATTRIBUTES, myHighlighters); - } - - if (!tryToRepairOldCursor(oldCursorRange)) { - LiveOccurrence afterCaret = firstOccurrenceAfterCaret(); - if (afterCaret != null) { - setCursor(afterCaret); - } else { - LiveOccurrence occurrence = firstVisibleOccurrence(); - if (occurrence != null) { - setCursor(occurrence); - } - } - } - } - - @Nullable - private LiveOccurrence firstVisibleOccurrence() { - int offset = Integer.MAX_VALUE; - LiveOccurrence firstOccurrence = null; - LiveOccurrence firstVisibleOccurrence = null; - for (LiveOccurrence o : mySearchResults) { - if (insideVisibleArea(myEditor, o.getPrimaryRange())) { - if (firstVisibleOccurrence == null || o.getPrimaryRange().getStartOffset() < firstVisibleOccurrence.getPrimaryRange().getStartOffset()) { - firstVisibleOccurrence = o; - } - } - if (o.getPrimaryRange().getStartOffset() < offset) { - offset = o.getPrimaryRange().getStartOffset(); - firstOccurrence = o; - } - } - return firstVisibleOccurrence != null ? firstVisibleOccurrence : firstOccurrence; - } - - @Nullable - private LiveOccurrence firstOccurrenceAfterCaret() { - LiveOccurrence afterCaret = null; - int caret = myEditor.getCaretModel().getOffset(); - for (LiveOccurrence occurrence : mySearchResults) { - if (occurrence.getPrimaryRange().getStartOffset() >= caret) { - if (afterCaret == null || occurrence.getPrimaryRange().getStartOffset() < afterCaret.getPrimaryRange().getStartOffset() ) { - afterCaret = occurrence; - } - } - } - return afterCaret; - } - - private boolean tryToRepairOldCursor(TextRange oldCursorRange) { - if (oldCursorRange == null) return false; - LiveOccurrence mayBeOldCursor = null; - for (LiveOccurrence searchResult : mySearchResults) { - if (searchResult.getPrimaryRange().intersects(oldCursorRange)) { - mayBeOldCursor = searchResult; - break; - } - } - if (mayBeOldCursor != null && insideVisibleArea(myEditor, mayBeOldCursor.getPrimaryRange())) { - setCursor(mayBeOldCursor); - return true; - } - return false; - } - - @Nullable - private LiveOccurrence prevOccurrence(LiveOccurrence o) { - if (mySearchResults == null) return null; - for (int i = 0; i < mySearchResults.size(); ++i) { - if (o == mySearchResults.get(i)) { - if (i > 0) { - return mySearchResults.get(i-1); - } - } - } - return null; - } - - @Nullable - private LiveOccurrence nextOccurrence(LiveOccurrence o) { - if (mySearchResults == null) return null; - boolean found = false; - for (LiveOccurrence occurrence : mySearchResults) { - if (found) { - return occurrence; - } - if (o == occurrence) { - found = true; - } - } - return null; - } - - public void prevOccurrence() { - LiveOccurrence prev = prevOccurrence(myCursor); - if (prev == null && !mySearchResults.isEmpty()) { - prev = mySearchResults.get(mySearchResults.size()-1); - } - moveCursorTo(prev); - } - - public void nextOccurrence() { - LiveOccurrence next = nextOccurrence(myCursor); - if (next == null && !mySearchResults.isEmpty()) { - next = mySearchResults.get(0); - } - moveCursorTo(next); - } - - public void moveCursorTo(LiveOccurrence next) { - if (next != null) { - setCursor(next); - } else { - showReplacementPreview(); - } - } - - private void setCursor(LiveOccurrence liveOccurrence) { - hideBalloon(); - boolean toNotify = myCursor == null || !myCursor.equals(liveOccurrence); - - myCursor = liveOccurrence; - - if (myCursorHighlighter != null) { - HighlightManager.getInstance(myProject).removeSegmentHighlighter(myEditor, myCursorHighlighter); - myCursorHighlighter = null; - } - if (myCursor != null) { - ArrayList dummy = new ArrayList(); - highlightRange(myCursor.getPrimaryRange(), new TextAttributes(null, null, null, null, 0), dummy); - if (!dummy.isEmpty()) { - myCursorHighlighter = dummy.get(0); - myCursorHighlighter.setCustomRenderer(new MyCustomCursorRenderer()); - } - - if (!insideVisibleArea(myEditor, myCursor.getPrimaryRange())) { - myEditor.getScrollingModel().scrollTo(myEditor.offsetToLogicalPosition(myCursor.getPrimaryRange().getStartOffset()), - ScrollType.CENTER); - myEditor.getScrollingModel().runActionOnScrollingFinished(new Runnable() { - @Override - public void run() { - showReplacementPreview(); - } - }); - } else { - showReplacementPreview(); - } - } - if (toNotify) { - for (CursorListener l : myListeners) { - if (l!= null) { - l.cursorMoved(); - } - } - } - } - - private void showReplacementPreview() { - hideBalloon(); - if (myDelegate != null && myCursor != null) { - String replacementPreviewText = myDelegate.getReplacementPreviewText(myEditor, myCursor); - if (replacementPreviewText != null) { - - //JLabel balloonContent = new JLabel(replacementPreviewText); - //balloonContent.setForeground(Color.WHITE); - - ReplacementView replacementView = new ReplacementView(replacementPreviewText, myCursor); - replacementView.setDelegate(this); - - BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createBalloonBuilder(replacementView); - balloonBuilder.setFadeoutTime(0); - balloonBuilder.setFillColor(IdeTooltipManager.GRAPHITE_COLOR); - balloonBuilder.setAnimationCycle(0); - balloonBuilder.setHideOnClickOutside(false); - balloonBuilder.setHideOnKeyOutside(false); - balloonBuilder.setHideOnAction(false); - balloonBuilder.setCloseButtonEnabled(true); - myReplacementBalloon = balloonBuilder.createBalloon(); - final int startOffset = myCursor.getPrimaryRange().getStartOffset(); - final int endOffset = myCursor.getPrimaryRange().getEndOffset(); - - myReplacementBalloon.show(new PositionTracker(myEditor.getContentComponent()) { - @Override - public RelativePoint recalculateLocation(final Balloon object) { - Point startPoint = myEditor.visualPositionToXY(myEditor.offsetToVisualPosition(startOffset)); - Point endPoint = myEditor.visualPositionToXY(myEditor.offsetToVisualPosition(endOffset)); - Point point = new Point((startPoint.x + endPoint.x)/2, startPoint.y); - if (!insideVisibleArea(myEditor, myCursor.getPrimaryRange())) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - object.hide(); - } - }); - - VisibleAreaListener visibleAreaListener = new VisibleAreaListener() { - @Override - public void visibleAreaChanged(VisibleAreaEvent e) { - if (insideVisibleArea(myEditor, myCursor.getPrimaryRange())) { - showReplacementPreview(); - final VisibleAreaListener visibleAreaListener = this; - myEditor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener); - myVisibleAreaListenersToRemove.remove(visibleAreaListener); - } - } - }; - myEditor.getScrollingModel().addVisibleAreaListener(visibleAreaListener); - myVisibleAreaListenersToRemove.add(visibleAreaListener); - - } - return new RelativePoint(myEditor.getContentComponent(), point); - } - }, Balloon.Position.above); - } - } - } - - private void hideBalloon() { - if (myReplacementBalloon != null) { - myReplacementBalloon.hide(); - myReplacementBalloon = null; - } - } - - private void highlightRange(TextRange textRange, TextAttributes attributes, Collection highlighters) { - HighlightManager highlightManager = HighlightManager.getInstance(myProject); - if (highlightManager != null) { - highlightManager.addRangeHighlight(myEditor, - textRange.getStartOffset(), textRange.getEndOffset(), - attributes, false, highlighters); - } - } - - private static boolean insideVisibleArea(Editor e, TextRange r) { - Rectangle visibleArea = e.getScrollingModel().getVisibleArea(); - Point point = e.logicalPositionToXY(e.offsetToLogicalPosition(r.getStartOffset())); - - return visibleArea.contains(point); - } - - private static class MyCustomCursorRenderer implements CustomHighlighterRenderer { - @Override - public void paint(Editor editor, RangeHighlighter highlighter, Graphics g) { - Graphics2D g2d = (Graphics2D)g; - VisualPosition startVp = editor.offsetToVisualPosition(highlighter.getStartOffset()); - VisualPosition endVp = editor.offsetToVisualPosition(highlighter.getEndOffset()); - Point start = editor.visualPositionToXY(startVp); - Point end = editor.visualPositionToXY(endVp); - g2d.setColor(new Color(50, 50, 50)); - g2d.translate(0, start.y - 4); - Color c1 = new Color(220, 200, 130); - Color c2 = new Color(220, 170, 30); - UIUtil.drawSearchMatch(g2d, start.x- 1, end.x+ 1, editor.getLineHeight() +2* 4, c1, c2); - g2d.translate(0, -start.y + 4); - } - } -} diff --git a/platform/lang-impl/src/com/intellij/find/impl/LivePreviewController.java b/platform/lang-impl/src/com/intellij/find/impl/LivePreviewController.java deleted file mode 100644 index 0cd76024bbc9..000000000000 --- a/platform/lang-impl/src/com/intellij/find/impl/LivePreviewController.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.intellij.find.impl; - -import com.intellij.find.FindModel; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.fileEditor.TextEditor; -import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.UserActivityListener; -import com.intellij.ui.UserActivityWatcher; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.awt.event.WindowEvent; -import java.awt.event.WindowFocusListener; -import java.util.List; - -public class LivePreviewController implements LivePreview.Delegate, UserActivityListener { - - - private final FindDialog myFindDialog; - private final LivePreview myLivePreview; - private final UserActivityWatcher myWatcher = new MomentoUserActivityWatcher(); - - LivePreviewControllerBase myDelegate = new LivePreviewControllerBase(); - - private void updateDelegatesFindModel() { - FindModel currentModel = myFindDialog.getCurrentModel(); - myDelegate.setFindModel(currentModel); - } - - public LivePreviewController(FindDialog findDialog, LivePreview livePreview, Component toWatch) { - myFindDialog = findDialog; - myLivePreview = livePreview; - myLivePreview.setDelegate(this); - myWatcher.register(toWatch); - myWatcher.addUserActivityListener(this); - myFindDialog.getWindow().addWindowFocusListener(new WindowFocusListener() { - @Override - public void windowGainedFocus(WindowEvent windowEvent) { - myLivePreview.update(); - } - - @Override - public void windowLostFocus(WindowEvent windowEvent) { - } - }); - - JComponent contentPane = (JComponent)findDialog.getContentPane(); - new AnAction() { - - @Override - public void actionPerformed(AnActionEvent e) { - myLivePreview.nextOccurrence(); - } - }.registerCustomShortcutSet(KeyEvent.VK_F3, 0, contentPane); - - new AnAction() { - - @Override - public void actionPerformed(AnActionEvent e) { - myLivePreview.prevOccurrence(); - } - }.registerCustomShortcutSet(KeyEvent.VK_F3, KeyEvent.SHIFT_MASK, contentPane); - } - - public void cleanUp() { - myWatcher.removeUserActivityListener(this); - myLivePreview.cleanUp(); - } - - @NotNull - @Override - public List performSearchInBackgroundInReadAction(Editor editor) { - updateDelegatesFindModel(); - return myDelegate.performSearchInBackgroundInReadAction(editor); - } - - @Override - public void performReplaceAll(Editor e) { - updateDelegatesFindModel(); - myDelegate.performReplaceAll(e); - } - - @Override - public void getFocusBack() { - myFindDialog.getContentPane().requestFocus(); - } - - @Override - public Editor getEditor(Ref needToUpdate) { - Editor result = null; - FileEditorManagerEx instanceEx = FileEditorManagerEx.getInstanceEx(myLivePreview.getProject()); - if (instanceEx != null) { - VirtualFile currentFile = instanceEx.getCurrentFile(); - if (currentFile != null) { - FileEditor[] editors = instanceEx.getEditors(currentFile); - if (editors.length > 0) { - FileEditor fileEditor = editors[0]; - boolean focusedFound = false; - for (FileEditor e : editors) { - if (e instanceof TextEditor && ((TextEditor)e).getEditor().getContentComponent().hasFocus()) { - fileEditor = e; - focusedFound = true; - } - } - needToUpdate.set(true); - if (!focusedFound) { - for (FileEditor e : editors) { - if (e instanceof TextEditor && ((TextEditor)e).getEditor() == myLivePreview.getEditor()) { - needToUpdate.set(false); - } - } - } - if(needToUpdate.get() && fileEditor instanceof TextEditor) { - result = ((TextEditor)fileEditor).getEditor(); - } - } - } - } - return result; - } - - @Override - public TextRange performReplace(final LiveOccurrence occurrence, final String replacement, final Editor editor) { - updateDelegatesFindModel(); - return myDelegate.performReplace(occurrence, replacement, editor); - } - - @Override - public String getReplacementPreviewText(Editor editor, LiveOccurrence liveOccurrence) { - updateDelegatesFindModel(); - return myDelegate.getReplacementPreviewText(editor, liveOccurrence); - } - - - @Override - public void stateChanged() { - myLivePreview.update(); - } -} diff --git a/platform/lang-impl/src/com/intellij/find/impl/LivePreviewControllerBase.java b/platform/lang-impl/src/com/intellij/find/impl/LivePreviewControllerBase.java deleted file mode 100644 index 82c93f288eec..000000000000 --- a/platform/lang-impl/src/com/intellij/find/impl/LivePreviewControllerBase.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.intellij.find.impl; - -import com.intellij.find.FindManager; -import com.intellij.find.FindModel; -import com.intellij.find.FindResult; -import com.intellij.find.FindUtil; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -public class LivePreviewControllerBase implements LivePreview.Delegate { - - private static final String EMPTY_STRING_DISPLAY_TEXT = ""; - - private int myMatchesLimit = 100; - - public int getMatchesLimit() { - return myMatchesLimit; - } - - public void setMatchesLimit(int matchesLimit) { - myMatchesLimit = matchesLimit; - } - - private FindModel myFindModel; - - public FindModel getFindModel() { - return myFindModel; - } - - public void setFindModel(FindModel findModel) { - myFindModel = findModel; - } - - private static void findResultsToOccurrences(ArrayList results, Collection occurrences) { - for (FindResult r : results) { - LiveOccurrence occurrence = new LiveOccurrence(); - occurrence.setPrimaryRange(r); - occurrences.add(occurrence); - } - } - - @NotNull - @Override - public List performSearchInBackgroundInReadAction(Editor editor) { - ArrayList occurrences = new ArrayList(); - if (myFindModel != null) { - - TextRange r = myFindModel.isGlobal() ? new TextRange(0, Integer.MAX_VALUE) : - new TextRange(editor.getSelectionModel().getSelectionStart(), - editor.getSelectionModel().getSelectionEnd()); - if (r.getLength() == 0) { - r = new TextRange(0, Integer.MAX_VALUE); - } - int offset = r.getStartOffset(); - VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(editor.getDocument()); - final ArrayList results = new ArrayList(); - - while (true) { - FindManager findManager = FindManager.getInstance(editor.getProject()); - FindResult result = findManager.findString(editor.getDocument().getCharsSequence(), offset, myFindModel, virtualFile); - if (!result.isStringFound()) break; - int newOffset = result.getEndOffset(); - if (offset == newOffset || result.getEndOffset() > r.getEndOffset()) break; - offset = newOffset; - results.add(result); - - if (results.size() > myMatchesLimit) break; - } - if (results.size() < myMatchesLimit) { - - findResultsToOccurrences(results, occurrences); - } - - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - searchEndsWith(results.size()); - } - }); - } - return occurrences; - } - - protected void searchEndsWith(int size) { - - } - - - @Override - public String getReplacementPreviewText(Editor editor, LiveOccurrence liveOccurrence) { - String foundString = editor.getDocument().getText(liveOccurrence.getPrimaryRange()); - String documentText = editor.getDocument().getText(); - FindModel currentModel = myFindModel; - String stringToReplace = null; - - if (currentModel != null) { - if (currentModel.isReplaceState()) { - FindManager findManager = FindManager.getInstance(editor.getProject()); - try { - stringToReplace = findManager.getStringToReplace(foundString, currentModel, - liveOccurrence.getPrimaryRange().getStartOffset(), documentText); - } - catch (FindManager.MalformedReplacementStringException e) { - return null; - } - if (stringToReplace != null && stringToReplace.isEmpty()) { - stringToReplace = EMPTY_STRING_DISPLAY_TEXT; - } - } - } - return stringToReplace; - } - - @Override - public TextRange performReplace(final LiveOccurrence occurrence, final String replacement, final Editor editor) { - TextRange range = occurrence.getPrimaryRange(); - try { - return FindUtil.doReplace(editor.getProject(), editor.getDocument(), myFindModel, new FindResultImpl(range.getStartOffset(), range.getEndOffset()), - FindManager.getInstance(editor.getProject()).getStringToReplace(editor.getDocument().getText(range), myFindModel), true, new ArrayList>()); - } - catch (FindManager.MalformedReplacementStringException e) { - /**/ - } - return null; - } - - @Override - public void performReplaceAll(Editor e) { - FindUtil.replace(e.getProject(), e, 0, myFindModel); - } - - @Override - public void getFocusBack() {} - - @Override - public Editor getEditor(Ref needToUpdate) { - return null; - } - -} diff --git a/platform/lang-impl/src/com/intellij/find/impl/ReplacementView.java b/platform/lang-impl/src/com/intellij/find/impl/ReplacementView.java deleted file mode 100644 index 80d51ed6f8ea..000000000000 --- a/platform/lang-impl/src/com/intellij/find/impl/ReplacementView.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.intellij.find.impl; - -import com.intellij.ide.IdeTooltipManager; -import com.intellij.openapi.util.SystemInfo; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -public class ReplacementView extends JPanel { - - private String myReplacement; - private LiveOccurrence myOccurrence; - - public interface Delegate { - void performReplacement(LiveOccurrence occurrence, String replacement); - void performReplaceAll(); - } - - private Delegate delegate; - - public Delegate getDelegate() { - return delegate; - } - - public void setDelegate(Delegate delegate) { - this.delegate = delegate; - } - - public ReplacementView(final String replacement, final LiveOccurrence occurrence) { - myReplacement = replacement; - myOccurrence = occurrence; - JLabel jLabel = new JLabel(replacement); - jLabel.setForeground(Color.WHITE); - add(jLabel); - JButton replace = new JButton("Replace"); - replace.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - if (delegate != null) { - delegate.performReplacement(occurrence, replacement); - } - } - }); - replace.setMnemonic('R'); - add(replace); - JButton replaceAllButton = new JButton("Replace all"); - replaceAllButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - if (delegate != null) { - delegate.performReplaceAll(); - } - } - }); - replaceAllButton.setMnemonic('a'); - add(replaceAllButton); - setBackground(IdeTooltipManager.GRAPHITE_COLOR); - if (SystemInfo.isMac) { - Font f = getFont(); - setFont(f.deriveFont(f.getStyle(), f.getSize() - 4)); - } - } - -} diff --git a/platform/lang-impl/src/com/intellij/find/impl/LiveOccurrence.java b/platform/lang-impl/src/com/intellij/find/impl/livePreview/LiveOccurrence.java similarity index 87% rename from platform/lang-impl/src/com/intellij/find/impl/LiveOccurrence.java rename to platform/lang-impl/src/com/intellij/find/impl/livePreview/LiveOccurrence.java index bef2f3c6536c..a1ae6c3a4e60 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/LiveOccurrence.java +++ b/platform/lang-impl/src/com/intellij/find/impl/livePreview/LiveOccurrence.java @@ -1,4 +1,4 @@ -package com.intellij.find.impl; +package com.intellij.find.impl.livePreview; import com.intellij.openapi.util.TextRange; @@ -34,4 +34,9 @@ public class LiveOccurrence { } return false; } + + @Override + public int hashCode() { + return myPrimaryRange.hashCode(); + } } diff --git a/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreview.java b/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreview.java new file mode 100644 index 000000000000..8deac659bc64 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreview.java @@ -0,0 +1,336 @@ +/* + * Copyright 2000-2011 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.find.impl.livePreview; + + +import com.intellij.codeInsight.highlighting.HighlightManager; +import com.intellij.ide.IdeTooltipManager; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.editor.VisualPosition; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.VisibleAreaEvent; +import com.intellij.openapi.editor.event.VisibleAreaListener; +import com.intellij.openapi.editor.markup.CustomHighlighterRenderer; +import com.intellij.openapi.editor.markup.EffectType; +import com.intellij.openapi.editor.markup.RangeHighlighter; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.ui.popup.BalloonBuilder; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.Segment; +import com.intellij.openapi.util.TextRange; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.util.containers.HashSet; +import com.intellij.util.ui.PositionTracker; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +public class LivePreview extends DocumentAdapter implements ReplacementView.Delegate, SearchResults.SearchResultsListener { + + public interface Delegate { + + @Nullable + String getReplacementPreviewText(Editor editor, LiveOccurrence liveOccurrence); + + @Nullable + TextRange performReplace(LiveOccurrence occurrence, String replacement, Editor editor); + + void performReplaceAll(Editor e); + + void getFocusBack(); + + } + + private final Collection myHighlighters = new HashSet(); + + + private RangeHighlighter myCursorHighlighter; + private final List myVisibleAreaListenersToRemove = new ArrayList(); + + private static final TextAttributes EXCLUDED_TARGET_ATTRIBUTES = new TextAttributes(Color.BLACK, Color.YELLOW, + Color.BLACK, EffectType.STRIKEOUT, 0); + + private static final TextAttributes OTHER_TARGETS_ATTRIBUTES = new TextAttributes(Color.BLACK, Color.GREEN, null, null, 0); + + private static final TextAttributes MAIN_TARGET_ATTRIBUTES = new TextAttributes(Color.BLACK, Color.YELLOW, null, null, 0); + + private Delegate myDelegate; + + private SearchResults mySearchResults; + + private Balloon myReplacementBalloon; + + @Override + public void performReplacement(LiveOccurrence occurrence, String replacement) { + if (myDelegate != null) { + myDelegate.performReplace(occurrence, replacement, mySearchResults.getEditor()); + myDelegate.getFocusBack(); + } + } + + @Override + public void performReplaceAll() { + myDelegate.performReplaceAll(mySearchResults.getEditor()); + } + + public boolean isExcluded(LiveOccurrence occurrence) { + return mySearchResults.isExcluded(occurrence); + } + + public void exclude(LiveOccurrence occurrence) { + mySearchResults.exclude(occurrence); + myDelegate.getFocusBack(); + } + + public boolean hasMatches() { + return mySearchResults.hasMatches(); + } + + @Override + public void searchResultsUpdated(SearchResults sr) { + removeFromEditor(mySearchResults.getEditor()); + highlightUsages(); + updateCursorHighlighting(false); + } + + @Override + public void cursorMoved() { + updateCursorHighlighting(true); + } + + public void editorChanged(SearchResults sr, Editor oldEditor) { + removeFromEditor(mySearchResults.getEditor()); + oldEditor.getDocument().removeDocumentListener(this); + mySearchResults.getEditor().getDocument().addDocumentListener(this); + } + + private void updateCursorHighlighting(boolean scroll) { + hideBalloon(); + + if (myCursorHighlighter != null) { + HighlightManager.getInstance(mySearchResults.getProject()).removeSegmentHighlighter(mySearchResults.getEditor(), myCursorHighlighter); + myCursorHighlighter = null; + } + + LiveOccurrence cursor = mySearchResults.getCursor(); + Editor editor = mySearchResults.getEditor(); + if (cursor != null) { + ArrayList dummy = new ArrayList(); + highlightRange(cursor.getPrimaryRange(), new TextAttributes(null, null, null, null, 0), dummy); + if (!dummy.isEmpty()) { + myCursorHighlighter = dummy.get(0); + myCursorHighlighter.setCustomRenderer(new CursorRenderer()); + } + + if (!SearchResults.insideVisibleArea(editor, cursor.getPrimaryRange()) && scroll) { + editor.getScrollingModel().scrollTo(editor.offsetToLogicalPosition(cursor.getPrimaryRange().getStartOffset()), + ScrollType.CENTER); + editor.getScrollingModel().runActionOnScrollingFinished(new Runnable() { + @Override + public void run() { + showReplacementPreview(); + } + }); + } else { + showReplacementPreview(); + } + } + } + + public LivePreview(SearchResults searchResults) { + mySearchResults = searchResults; + searchResultsUpdated(searchResults); + searchResults.addListener(this); + } + + public Delegate getDelegate() { + return myDelegate; + } + + public void setDelegate(Delegate delegate) { + myDelegate = delegate; + } + + + public void cleanUp() { + removeFromEditor(mySearchResults.getEditor()); + } + + private void removeFromEditor(Editor editor) { + if (myReplacementBalloon != null) { + myReplacementBalloon.hide(); + } + + if (editor != null) { + + for (VisibleAreaListener visibleAreaListener : myVisibleAreaListenersToRemove) { + editor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener); + } + myVisibleAreaListenersToRemove.clear(); + for (RangeHighlighter h : myHighlighters) { + HighlightManager.getInstance(mySearchResults.getProject()).removeSegmentHighlighter(editor, h); + } + if (myCursorHighlighter != null) { + HighlightManager.getInstance(mySearchResults.getProject()).removeSegmentHighlighter(editor, myCursorHighlighter); + myCursorHighlighter = null; + } + } + } + + private void highlightUsages() { + if (mySearchResults.getEditor() == null) return; + for (LiveOccurrence o : mySearchResults.getOccurrences()) { + for (TextRange textRange : o.getSecondaryRanges()) { + highlightRange(textRange, OTHER_TARGETS_ATTRIBUTES, myHighlighters); + } + if (mySearchResults.isExcluded(o)) { + highlightRange(o.getPrimaryRange(), EXCLUDED_TARGET_ATTRIBUTES, myHighlighters); + } else { + highlightRange(o.getPrimaryRange(), MAIN_TARGET_ATTRIBUTES, myHighlighters); + } + } + } + + private void showReplacementPreview() { + hideBalloon(); + final LiveOccurrence cursor = mySearchResults.getCursor(); + final Editor editor = mySearchResults.getEditor(); + if (myDelegate != null && cursor != null) { + String replacementPreviewText = myDelegate.getReplacementPreviewText(editor, cursor); + if (replacementPreviewText != null) { + + ReplacementView replacementView = new ReplacementView(replacementPreviewText, cursor); + replacementView.setDelegate(this); + + BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createBalloonBuilder(replacementView); + balloonBuilder.setFadeoutTime(0); + balloonBuilder.setFillColor(IdeTooltipManager.GRAPHITE_COLOR); + balloonBuilder.setAnimationCycle(0); + balloonBuilder.setHideOnClickOutside(false); + balloonBuilder.setHideOnKeyOutside(false); + balloonBuilder.setHideOnAction(false); + balloonBuilder.setCloseButtonEnabled(true); + myReplacementBalloon = balloonBuilder.createBalloon(); + final int startOffset = cursor.getPrimaryRange().getStartOffset(); + final int endOffset = cursor.getPrimaryRange().getEndOffset(); + + myReplacementBalloon.show(new ReplacementBalloonPositionTracker(editor, startOffset, endOffset, cursor), Balloon.Position.above); + } + } + } + + private void hideBalloon() { + if (myReplacementBalloon != null) { + myReplacementBalloon.hide(); + myReplacementBalloon = null; + } + } + + private void highlightRange(TextRange textRange, TextAttributes attributes, Collection highlighters) { + HighlightManager highlightManager = HighlightManager.getInstance(mySearchResults.getProject()); + if (highlightManager != null) { + highlightManager.addRangeHighlight(mySearchResults.getEditor(), + textRange.getStartOffset(), textRange.getEndOffset(), + attributes, false, highlighters); + } + } + + + + private static class CursorRenderer implements CustomHighlighterRenderer { + @Override + public void paint(Editor editor, RangeHighlighter highlighter, Graphics g) { + Document document = editor.getDocument(); + int offset = highlighter.getStartOffset(); + while (offset < highlighter.getEndOffset()) { + int line = document.getLineNumber(offset); + int newOffset = document.getLineEndOffset(line); + newOffset = Math.min(highlighter.getEndOffset(), newOffset); + drawSegment(editor, new TextRange(offset, newOffset), g); + offset = newOffset+1; + } + + } + + private static void drawSegment(Editor editor, Segment highlighter, Graphics g) { + Graphics2D g2d = (Graphics2D)g; + VisualPosition startVp = editor.offsetToVisualPosition(highlighter.getStartOffset()); + VisualPosition endVp = editor.offsetToVisualPosition(highlighter.getEndOffset()); + Point start = editor.visualPositionToXY(startVp); + Point end = editor.visualPositionToXY(endVp); + g2d.setColor(new Color(50, 50, 50)); + g2d.translate(0, start.y - 4); + Color c1 = new Color(220, 200, 130); + Color c2 = new Color(220, 170, 30); + int endX = start.x != end.x ? end.x : end.x + 2; + UIUtil.drawSearchMatch(g2d, start.x - 1, endX + 1, editor.getLineHeight() + 2 * 4, c1, c2); + g2d.translate(0, -start.y + 4); + } + } + + private class ReplacementBalloonPositionTracker extends PositionTracker { + private final Editor myEditor; + private final int myStartOffset; + private final int myEndOffset; + private final LiveOccurrence myCursor; + + public ReplacementBalloonPositionTracker(Editor editor, int startOffset, int endOffset, LiveOccurrence cursor) { + super(editor.getContentComponent()); + myEditor = editor; + myStartOffset = startOffset; + myEndOffset = endOffset; + myCursor = cursor; + } + + @Override + public RelativePoint recalculateLocation(final Balloon object) { + Point startPoint = myEditor.visualPositionToXY(myEditor.offsetToVisualPosition(myStartOffset)); + Point endPoint = myEditor.visualPositionToXY(myEditor.offsetToVisualPosition(myEndOffset)); + Point point = new Point((startPoint.x + endPoint.x)/2, startPoint.y); + if (!SearchResults.insideVisibleArea(myEditor, myCursor.getPrimaryRange())) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + object.hide(); + } + }); + + VisibleAreaListener visibleAreaListener = new VisibleAreaListener() { + @Override + public void visibleAreaChanged(VisibleAreaEvent e) { + if (SearchResults.insideVisibleArea(myEditor, myCursor.getPrimaryRange())) { + showReplacementPreview(); + final VisibleAreaListener visibleAreaListener = this; + myEditor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener); + myVisibleAreaListenersToRemove.remove(visibleAreaListener); + } + } + }; + myEditor.getScrollingModel().addVisibleAreaListener(visibleAreaListener); + myVisibleAreaListenersToRemove.add(visibleAreaListener); + + } + return new RelativePoint(myEditor.getContentComponent(), point); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreviewControllerBase.java b/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreviewControllerBase.java new file mode 100644 index 000000000000..a94ec03b51a1 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreviewControllerBase.java @@ -0,0 +1,119 @@ +package com.intellij.find.impl.livePreview; + +import com.intellij.find.FindManager; +import com.intellij.find.FindModel; +import com.intellij.find.FindUtil; +import com.intellij.find.impl.FindResultImpl; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.util.Alarm; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; + +public class LivePreviewControllerBase implements LivePreview.Delegate, FindUtil.ReplaceDelegate { + + private static final String EMPTY_STRING_DISPLAY_TEXT = ""; + + private static final int USER_ACTIVITY_TRIGGERING_DELAY = 300; + + private int myUserActivityDelay = USER_ACTIVITY_TRIGGERING_DELAY; + + private final Alarm myLivePreviewAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); + + private SearchResults mySearchResults; + private LivePreview myLivePreview; + + public LivePreviewControllerBase(SearchResults searchResults, LivePreview livePreview) { + mySearchResults = searchResults; + myLivePreview = livePreview; + myLivePreview.setDelegate(this); + } + + public int getUserActivityDelay() { + return myUserActivityDelay; + } + + public void setUserActivityDelay(int userActivityDelay) { + myUserActivityDelay = userActivityDelay; + } + + public void updateInBackground(final FindModel findModel) { + myLivePreviewAlarm.cancelAllRequests(); + if (findModel == null) return; + Runnable request = new Runnable() { + @Override + public void run() { + mySearchResults.updateThreadSafe(findModel); + } + }; + if (ApplicationManager.getApplication().isUnitTestMode()) { + request.run(); + } else { + myLivePreviewAlarm.addRequest(request, myUserActivityDelay); + } + } + + @Override + public String getReplacementPreviewText(Editor editor, LiveOccurrence liveOccurrence) { + String foundString = editor.getDocument().getText(liveOccurrence.getPrimaryRange()); + String documentText = editor.getDocument().getText(); + FindModel currentModel = mySearchResults.getFindModel(); + String stringToReplace = null; + + if (currentModel != null) { + if (currentModel.isReplaceState()) { + FindManager findManager = FindManager.getInstance(editor.getProject()); + try { + stringToReplace = findManager.getStringToReplace(foundString, currentModel, + liveOccurrence.getPrimaryRange().getStartOffset(), documentText); + } + catch (FindManager.MalformedReplacementStringException e) { + return null; + } + if (stringToReplace != null && stringToReplace.isEmpty()) { + stringToReplace = EMPTY_STRING_DISPLAY_TEXT; + } + } + } + return stringToReplace; + } + + @Nullable + @Override + public TextRange performReplace(final LiveOccurrence occurrence, final String replacement, final Editor editor) { + TextRange range = occurrence.getPrimaryRange(); + FindModel findModel = mySearchResults.getFindModel(); + try { + return FindUtil.doReplace(editor.getProject(), editor.getDocument(), findModel, new FindResultImpl(range.getStartOffset(), range.getEndOffset()), + FindManager.getInstance(editor.getProject()).getStringToReplace(editor.getDocument().getText(range), findModel), + true, new ArrayList>()); + } + catch (FindManager.MalformedReplacementStringException e) { + /**/ + } + mySearchResults.updateThreadSafe(findModel); + return null; + } + + @Override + public void performReplaceAll(Editor e) { + FindUtil.replace(e.getProject(), e, 0, mySearchResults.getFindModel(), this); + } + + @Override + public void getFocusBack() {} + + @Override + public boolean shouldReplace(TextRange range, String replace) { + for (LiveOccurrence o : mySearchResults.getExcluded()) { + TextRange primaryRange = o.getPrimaryRange(); + if (primaryRange.equals(range)) { + return false; + } + } + return true; + } +} diff --git a/platform/lang-impl/src/com/intellij/find/impl/livePreview/ReplacementView.java b/platform/lang-impl/src/com/intellij/find/impl/livePreview/ReplacementView.java new file mode 100644 index 000000000000..ea66fc678db6 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/impl/livePreview/ReplacementView.java @@ -0,0 +1,125 @@ +package com.intellij.find.impl.livePreview; + +import com.intellij.ide.IdeTooltipManager; +import com.intellij.openapi.util.SystemInfo; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ReplacementView extends JPanel { + + private String myReplacement; + private LiveOccurrence myOccurrence; + private JButton myStatusButton; + + public interface Delegate { + void performReplacement(LiveOccurrence occurrence, String replacement); + void performReplaceAll(); + boolean isExcluded(LiveOccurrence occurrence); + void exclude(LiveOccurrence occurrence); + } + + private Delegate myDelegate; + + public Delegate getDelegate() { + return myDelegate; + } + + public void setDelegate(Delegate delegate) { + this.myDelegate = delegate; + updateStatusLabel(); + } + + @Override + protected void paintComponent(Graphics graphics) { + + } + + public ReplacementView(final String replacement, final LiveOccurrence occurrence) { + myReplacement = replacement; + myOccurrence = occurrence; + setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + + JLabel jLabel = new JLabel(replacement); + jLabel.setForeground(Color.WHITE); + JPanel labelPane = new JPanel(new FlowLayout()) { + @Override + protected void paintComponent(Graphics graphics) {} + }; + labelPane.setAlignmentX(Component.LEFT_ALIGNMENT); + labelPane.add(jLabel); + + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = c.gridy = 0; + c.gridwidth = 2; + c.gridheight = 1; + add(labelPane, c); + JButton replace = new JButton("Replace"); + + replace.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + if (myDelegate != null) { + myDelegate.performReplacement(occurrence, replacement); + } + } + }); + replace.setPreferredSize(new Dimension(80, 20)); + replace.setMnemonic('R'); + JPanel buttonsPane = new JPanel(){ + @Override + protected void paintComponent(Graphics graphics) { } + }; + JButton replaceAllButton = new JButton("Replace all"); + + replaceAllButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + if (myDelegate != null) { + myDelegate.performReplaceAll(); + } + } + }); + replaceAllButton.setPreferredSize(new Dimension(100, 20)); + replaceAllButton.setMnemonic('a'); + + myStatusButton = new JButton("Exclude"); + myStatusButton.setPreferredSize(new Dimension(80, 20)); + myStatusButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + myDelegate.exclude(myOccurrence); + updateStatusLabel(); + } + }); + myStatusButton.setMnemonic('l'); + buttonsPane.add(myStatusButton); + + setBackground(IdeTooltipManager.GRAPHITE_COLOR); + if (SystemInfo.isMac) { + Font f = getFont(); + setFont(f.deriveFont(f.getStyle(), f.getSize() - 4)); + } + + buttonsPane.add(replace); + buttonsPane.add(replaceAllButton); + + c.gridx = 0; + c.gridy = 1; + + c.gridwidth = 3; + c.gridheight = 1; + add(buttonsPane, c); + setAlignmentX(Component.LEFT_ALIGNMENT); + } + + private void updateStatusLabel() { + myStatusButton.setText(myDelegate.isExcluded(myOccurrence) ? "Include" : "Exclude"); + myStatusButton.repaint(); + } + +} diff --git a/platform/lang-impl/src/com/intellij/find/impl/livePreview/SearchResults.java b/platform/lang-impl/src/com/intellij/find/impl/livePreview/SearchResults.java new file mode 100644 index 000000000000..d6925ef5d915 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/impl/livePreview/SearchResults.java @@ -0,0 +1,328 @@ +package com.intellij.find.impl.livePreview; + + +import com.intellij.find.FindManager; +import com.intellij.find.FindModel; +import com.intellij.find.FindResult; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.util.*; +import java.util.List; + +public class SearchResults { + + private int myActualFound = 0; + + private List myListeners = new ArrayList(); + + private LiveOccurrence myCursor; + + private List myOccurrences = new ArrayList(); + private Set myExcluded = new HashSet(); + + private Editor myEditor; + + private FindModel myFindModel; + + private int myMatchesLimit = 100; + + public SearchResults(Editor editor) { + myEditor = editor; + } + + public int getActualFound() { + return myActualFound; + } + + public boolean hasMatches() { + return !getOccurrences().isEmpty(); + } + + public FindModel getFindModel() { + return myFindModel; + } + + public boolean isExcluded(LiveOccurrence occurrence) { + return myExcluded.contains(occurrence); + } + + public void exclude(LiveOccurrence occurrence) { + if (myExcluded.contains(occurrence)) { + myExcluded.remove(occurrence); + } else { + myExcluded.add(occurrence); + } + notifyChanged(); + } + + public Set getExcluded() { + return myExcluded; + } + + public interface SearchResultsListener { + void searchResultsUpdated(SearchResults sr); + void editorChanged(SearchResults sr, Editor oldEditor); + + void cursorMoved(); + } + + public void addListener(SearchResultsListener srl) { + myListeners.add(srl); + } + + public void removeListener(SearchResultsListener srl) { + myListeners.remove(srl); + } + + public int getMatchesLimit() { + return myMatchesLimit; + } + + public void setMatchesLimit(int matchesLimit) { + myMatchesLimit = matchesLimit; + } + + public LiveOccurrence getCursor() { + return myCursor; + } + + public List getOccurrences() { + return myOccurrences; + } + + public Project getProject() { + return myEditor.getProject(); + } + + public synchronized void setEditor(Editor editor) { + Editor oldOne = myEditor; + myEditor = editor; + notifyEditorChanged(oldOne); + } + + private void notifyEditorChanged(Editor oldOne) { + for (SearchResultsListener listener : myListeners) { + listener.editorChanged(this, oldOne); + } + } + + public synchronized Editor getEditor() { + return myEditor; + } + + private static void findResultsToOccurrences(ArrayList results, Collection occurrences) { + for (FindResult r : results) { + LiveOccurrence occurrence = new LiveOccurrence(); + occurrence.setPrimaryRange(r); + occurrences.add(occurrence); + } + } + + public void updateThreadSafe(final FindModel findModel) { + final ArrayList occurrences = new ArrayList(); + final Editor editor = getEditor(); + + final ArrayList results = new ArrayList(); + if (findModel != null) { + + TextRange r = findModel.isGlobal() ? new TextRange(0, Integer.MAX_VALUE) : + new TextRange(editor.getSelectionModel().getSelectionStart(), + editor.getSelectionModel().getSelectionEnd()); + if (r.getLength() == 0) { + r = new TextRange(0, Integer.MAX_VALUE); + } + int offset = r.getStartOffset(); + VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(editor.getDocument()); + + while (true) { + FindManager findManager = FindManager.getInstance(editor.getProject()); + FindResult result = findManager.findString(editor.getDocument().getCharsSequence(), offset, findModel, virtualFile); + if (!result.isStringFound()) break; + int newOffset = result.getEndOffset(); + if (offset == newOffset || result.getEndOffset() > r.getEndOffset()) break; + offset = newOffset; + results.add(result); + + if (results.size() > myMatchesLimit) break; + } + if (results.size() < myMatchesLimit) { + + findResultsToOccurrences(results, occurrences); + } + } + + final Runnable r = new Runnable() { + @Override + public void run() { + searchCompleted(occurrences, results.size(), editor, findModel); + } + }; + + if (!ApplicationManager.getApplication().isUnitTestMode()) { + ApplicationManager.getApplication().invokeLater(r); + } else { + r.run(); + } + } + + private void searchCompleted(List occurrences, int size, Editor editor, FindModel findModel) { + if (editor == getEditor()) { + myOccurrences = occurrences; + final TextRange oldCursorRange = myCursor != null ? myCursor.getPrimaryRange() : null; + Collections.sort(myOccurrences, new Comparator() { + @Override + public int compare(LiveOccurrence liveOccurrence, LiveOccurrence liveOccurrence1) { + return liveOccurrence.getPrimaryRange().getStartOffset() - liveOccurrence1.getPrimaryRange().getStartOffset(); + } + }); + + updateCursor(oldCursorRange); + myFindModel = findModel; + myActualFound = size; + notifyChanged(); + if (oldCursorRange == null) { + notifyCursorMoved(); + } + } + } + + private void updateCursor(TextRange oldCursorRange) { + if (!tryToRepairOldCursor(oldCursorRange)) { + LiveOccurrence afterCaret = firstOccurrenceAfterCaret(); + if (afterCaret != null) { + myCursor = afterCaret; + } else { + LiveOccurrence occurrence = firstVisibleOccurrence(); + myCursor = occurrence; + } + } + } + + private void notifyChanged() { + for (SearchResultsListener listener : myListeners) { + listener.searchResultsUpdated(this); + } + } + + static boolean insideVisibleArea(Editor e, TextRange r) { + Rectangle visibleArea = e.getScrollingModel().getVisibleArea(); + Point point = e.logicalPositionToXY(e.offsetToLogicalPosition(r.getStartOffset())); + + return visibleArea.contains(point); + } + + @Nullable + private LiveOccurrence firstVisibleOccurrence() { + int offset = Integer.MAX_VALUE; + LiveOccurrence firstOccurrence = null; + LiveOccurrence firstVisibleOccurrence = null; + for (LiveOccurrence o : getOccurrences()) { + if (insideVisibleArea(myEditor, o.getPrimaryRange())) { + if (firstVisibleOccurrence == null || o.getPrimaryRange().getStartOffset() < firstVisibleOccurrence.getPrimaryRange().getStartOffset()) { + firstVisibleOccurrence = o; + } + } + if (o.getPrimaryRange().getStartOffset() < offset) { + offset = o.getPrimaryRange().getStartOffset(); + firstOccurrence = o; + } + } + return firstVisibleOccurrence != null ? firstVisibleOccurrence : firstOccurrence; + } + + @Nullable + private LiveOccurrence firstOccurrenceAfterCaret() { + LiveOccurrence afterCaret = null; + int caret = myEditor.getCaretModel().getOffset(); + for (LiveOccurrence occurrence : getOccurrences()) { + if (occurrence.getPrimaryRange().getStartOffset() >= caret) { + if (afterCaret == null || occurrence.getPrimaryRange().getStartOffset() < afterCaret.getPrimaryRange().getStartOffset() ) { + afterCaret = occurrence; + } + } + } + return afterCaret; + } + + private boolean tryToRepairOldCursor(TextRange oldCursorRange) { + if (oldCursorRange == null) return false; + LiveOccurrence mayBeOldCursor = null; + for (LiveOccurrence searchResult : getOccurrences()) { + if (searchResult.getPrimaryRange().intersects(oldCursorRange)) { + mayBeOldCursor = searchResult; + break; + } + } + if (mayBeOldCursor != null) { + myCursor = mayBeOldCursor; + return true; + } + return false; + } + + @Nullable + private LiveOccurrence prevOccurrence(LiveOccurrence o) { + for (int i = 0; i < getOccurrences().size(); ++i) { + if (o == getOccurrences().get(i)) { + if (i > 0) { + return getOccurrences().get(i - 1); + } + } + } + return null; + } + + @Nullable + private LiveOccurrence nextOccurrence(LiveOccurrence o) { + boolean found = false; + for (LiveOccurrence occurrence : getOccurrences()) { + if (found) { + return occurrence; + } + if (o == occurrence) { + found = true; + } + } + return null; + } + + public void prevOccurrence() { + LiveOccurrence prev = prevOccurrence(myCursor); + if (prev == null && !getOccurrences().isEmpty()) { + prev = getOccurrences().get(getOccurrences().size() - 1); + } + moveCursorTo(prev); + } + + public void nextOccurrence() { + LiveOccurrence next = nextOccurrence(myCursor); + if (next == null && !getOccurrences().isEmpty()) { + next = getOccurrences().get(0); + } + moveCursorTo(next); + } + + public void moveCursorTo(LiveOccurrence next) { + if (next != null) { + setCursor(next); + } + } + + private void setCursor(LiveOccurrence liveOccurrence) { + myCursor = liveOccurrence; + notifyCursorMoved(); + } + + private void notifyCursorMoved() { + for (SearchResultsListener listener : myListeners) { + listener.cursorMoved(); + } + } +} From 5fac316fdcd36455280ef5f7cd5156d5f155c805 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 18 Feb 2011 20:07:53 +0100 Subject: [PATCH 17/38] IDEA-65704 ("Replace for-each loop with indexed for loop" breaks code) --- .../ReplaceForEachLoopWithIndexedForLoopIntention.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/forloop/ReplaceForEachLoopWithIndexedForLoopIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/forloop/ReplaceForEachLoopWithIndexedForLoopIntention.java index cb3bace8866f..2b255c1969aa 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/forloop/ReplaceForEachLoopWithIndexedForLoopIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/forloop/ReplaceForEachLoopWithIndexedForLoopIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -181,7 +181,8 @@ public class ReplaceForEachLoopWithIndexedForLoopIntention extends Intention { PsiExpression expression, PsiElement context) { if (expression instanceof PsiMethodCallExpression || expression instanceof PsiTypeCastExpression || - expression instanceof PsiArrayAccessExpression) { + expression instanceof PsiArrayAccessExpression || + expression instanceof PsiNewExpression) { final String variableName = getVariableName(expression); return createVariable(variableName, expression, context); } else if (expression instanceof PsiParenthesizedExpression) { From fcc2520caeb381aba72e3047395894f915dd0ba6 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 18 Feb 2011 20:47:34 +0100 Subject: [PATCH 18/38] Replace if with switch intention improvements: prevent unnecessary braces around switch branches if braces are inserted place the break statement inside the braces smaller cleaner code --- .../siyeh/ipp/psiutils/DeclarationUtils.java | 150 ------------------ .../ipp/switchtoif/IfStatementBranch.java | 91 ++++++----- .../ReplaceIfWithSwitchIntention.java | 80 ++++------ 3 files changed, 74 insertions(+), 247 deletions(-) delete mode 100644 plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/DeclarationUtils.java diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/DeclarationUtils.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/DeclarationUtils.java deleted file mode 100644 index 4e8c53f5d9a7..000000000000 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/DeclarationUtils.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2003-2005 Dave Griffith - * - * 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.siyeh.ipp.psiutils; - -import com.intellij.psi.*; - -import java.util.Set; - -public class DeclarationUtils{ - - private DeclarationUtils(){ - super(); - } - - public static void calculateVariablesDeclared( - PsiStatement statement, Set variablesDeclaredAtTopLevel, - Set variablesDeclaredAtLowerLevels, boolean isTopLevel){ - if(statement == null){ - return; - } - if(statement instanceof PsiBreakStatement || - statement instanceof PsiExpressionStatement || - statement instanceof PsiContinueStatement || - statement instanceof PsiThrowStatement || - statement instanceof PsiExpressionListStatement || - statement instanceof PsiAssertStatement || - statement instanceof PsiReturnStatement){ - } else if(statement instanceof PsiDeclarationStatement){ - final PsiDeclarationStatement declStatement = - (PsiDeclarationStatement) statement; - final PsiElement[] elements = declStatement.getDeclaredElements(); - for(PsiElement element : elements){ - final PsiVariable var = (PsiVariable) element; - final String varName = var.getName(); - if(isTopLevel){ - variablesDeclaredAtTopLevel.add(varName); - } else{ - variablesDeclaredAtLowerLevels.add(varName); - } - } - } else if(statement instanceof PsiForStatement){ - final PsiForStatement loopStatement = (PsiForStatement) statement; - final PsiStatement initialization = - loopStatement.getInitialization(); - calculateVariablesDeclared(initialization, - variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - final PsiStatement update = loopStatement.getUpdate(); - calculateVariablesDeclared(update, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - final PsiStatement body = loopStatement.getBody(); - calculateVariablesDeclared(body, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - } else if(statement instanceof PsiWhileStatement){ - final PsiWhileStatement loopStatement = - (PsiWhileStatement) statement; - final PsiStatement body = loopStatement.getBody(); - calculateVariablesDeclared(body, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - } else if(statement instanceof PsiDoWhileStatement){ - final PsiDoWhileStatement loopStatement = - (PsiDoWhileStatement) statement; - final PsiStatement body = loopStatement.getBody(); - calculateVariablesDeclared(body, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - } else if(statement instanceof PsiSynchronizedStatement){ - final PsiSynchronizedStatement syncStatement = - (PsiSynchronizedStatement) statement; - final PsiCodeBlock body = syncStatement.getBody(); - calculateVariablesDeclaredInCodeBlock( - body, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - } else if(statement instanceof PsiBlockStatement){ - final PsiBlockStatement block = (PsiBlockStatement) statement; - final PsiCodeBlock codeBlock = block.getCodeBlock(); - calculateVariablesDeclaredInCodeBlock( - codeBlock, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, isTopLevel); - } else if(statement instanceof PsiLabeledStatement){ - final PsiLabeledStatement labeledStatement = - (PsiLabeledStatement) statement; - final PsiStatement body = labeledStatement.getStatement(); - calculateVariablesDeclared(body, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - } else if(statement instanceof PsiIfStatement){ - final PsiIfStatement ifStatement = (PsiIfStatement) statement; - final PsiStatement thenBranch = ifStatement.getThenBranch(); - calculateVariablesDeclared(thenBranch, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - final PsiStatement elseBranch = ifStatement.getElseBranch(); - calculateVariablesDeclared(elseBranch, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - } else if(statement instanceof PsiTryStatement){ - final PsiTryStatement tryStatement = (PsiTryStatement) statement; - final PsiCodeBlock tryBlock = tryStatement.getTryBlock(); - calculateVariablesDeclaredInCodeBlock( - tryBlock, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - calculateVariablesDeclaredInCodeBlock( - tryBlock, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); - if(finallyBlock != null){ - calculateVariablesDeclaredInCodeBlock( - finallyBlock, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - } - final PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks(); - for(PsiCodeBlock catchBlock : catchBlocks){ - calculateVariablesDeclaredInCodeBlock( - catchBlock, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - } - } else if(statement instanceof PsiSwitchStatement){ - final PsiSwitchStatement switchStatement = - (PsiSwitchStatement) statement; - final PsiCodeBlock body = switchStatement.getBody(); - calculateVariablesDeclaredInCodeBlock( - body, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, false); - } - } - - private static void calculateVariablesDeclaredInCodeBlock( - PsiCodeBlock block, Set variablesDeclaredAtTopLevel, - SetvariablesDeclaredAtLowerLevels, boolean isTopLevel){ - if(block == null){ - return; - } - final PsiStatement[] statements = block.getStatements(); - for(PsiStatement statement : statements){ - calculateVariablesDeclared( - statement, variablesDeclaredAtTopLevel, - variablesDeclaredAtLowerLevels, isTopLevel); - } - } -} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfStatementBranch.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfStatementBranch.java index 67eb341a247e..532998081963 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfStatementBranch.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfStatementBranch.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,21 +15,23 @@ */ package com.siyeh.ipp.switchtoif; -import com.intellij.psi.PsiStatement; +import com.intellij.psi.*; import java.util.*; class IfStatementBranch{ - private Set m_topLevelVariables = new HashSet(3); - private Set m_innerVariables = new HashSet(3); + private final Set topLevelVariables = new HashSet(3); private final LinkedList comments = new LinkedList(); private final LinkedList statementComments = new LinkedList(); - private final List m_conditions = new ArrayList(3); - private PsiStatement m_statement = null; - private boolean m_else = false; + private final List conditions = new ArrayList(3); + private final PsiStatement statement; + private final boolean elseBranch; - public IfStatementBranch() { + public IfStatementBranch(PsiStatement branch, boolean elseBranch) { + statement = branch; + this.elseBranch = elseBranch; + calculateVariablesDeclared(statement); } public void addComment(String comment){ @@ -41,58 +43,31 @@ class IfStatementBranch{ } public void addCondition(String conditionString){ - m_conditions.add(conditionString); - } - - public void setStatement(PsiStatement statement){ - m_statement = statement; + conditions.add(conditionString); } public PsiStatement getStatement(){ - return m_statement; + return statement; } public List getConditions(){ - return Collections.unmodifiableList(m_conditions); + return Collections.unmodifiableList(conditions); } public boolean isElse(){ - return m_else; + return elseBranch; } - public void setElse(){ - m_else = true; - } - - public void setTopLevelVariables(Set topLevelVariables){ - m_topLevelVariables = new HashSet(topLevelVariables); - } - - public void setInnerVariables(Set innerVariables){ - m_innerVariables = new HashSet(innerVariables); - } - - private Set getTopLevelVariables(){ - return Collections.unmodifiableSet(m_topLevelVariables); - } - - private Set getInnerVariables(){ - return Collections.unmodifiableSet(m_innerVariables); - } - - public boolean topLevelDeclarationsConfictWith( + public boolean topLevelDeclarationsConflictWith( IfStatementBranch testBranch){ - final Set innerVariables = testBranch.getInnerVariables(); - final Set topLevel = testBranch.getTopLevelVariables(); - return hasNonEmptyIntersection(m_topLevelVariables, topLevel) || - hasNonEmptyIntersection(m_topLevelVariables, - innerVariables); + final Set topLevel = testBranch.topLevelVariables; + return intersects(topLevelVariables, topLevel); } - private static boolean hasNonEmptyIntersection(Set set1, - Set set2){ - for(final String set1Element : set1){ - if(set2.contains(set1Element)){ + private static boolean intersects(Set set1, + Set set2){ + for(final String s : set1){ + if(set2.contains(s)){ return true; } } @@ -106,4 +81,28 @@ class IfStatementBranch{ public List getStatementComments() { return statementComments; } + + public void calculateVariablesDeclared(PsiStatement statement){ + if(statement == null){ + return; + } + if(statement instanceof PsiDeclarationStatement){ + final PsiDeclarationStatement declarationStatement = + (PsiDeclarationStatement) statement; + final PsiElement[] elements = + declarationStatement.getDeclaredElements(); + for(PsiElement element : elements){ + final PsiVariable variable = (PsiVariable) element; + final String varName = variable.getName(); + topLevelVariables.add(varName); + } + } else if(statement instanceof PsiBlockStatement){ + final PsiBlockStatement block = (PsiBlockStatement) statement; + final PsiCodeBlock codeBlock = block.getCodeBlock(); + final PsiStatement[] statements = codeBlock.getStatements(); + for(PsiStatement statement1 : statements){ + calculateVariablesDeclared(statement1); + } + } + } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java index 992c526173b4..fe3c56ec6933 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java @@ -22,16 +22,13 @@ import com.intellij.util.IncorrectOperationException; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; import com.siyeh.ipp.psiutils.ControlFlowUtils; -import com.siyeh.ipp.psiutils.DeclarationUtils; import com.siyeh.ipp.psiutils.EquivalenceChecker; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class ReplaceIfWithSwitchIntention extends Intention { @@ -79,25 +76,17 @@ public class ReplaceIfWithSwitchIntention extends Intention { final List branches = new ArrayList(20); while (true) { - final Set topLevelVariables = new HashSet(5); - final Set innerVariables = new HashSet(5); final PsiExpression condition = ifStatement.getCondition(); final List labels = getValuesFromExpression(condition, caseExpression, new ArrayList()); final PsiStatement thenBranch = ifStatement.getThenBranch(); - DeclarationUtils.calculateVariablesDeclared(thenBranch, - topLevelVariables, - innerVariables, - true); - final IfStatementBranch ifBranch = new IfStatementBranch(); + final IfStatementBranch ifBranch = + new IfStatementBranch(thenBranch, false); if (!branches.isEmpty()) { extractIfComments(ifStatement, ifBranch); } - ifBranch.setInnerVariables(innerVariables); - ifBranch.setTopLevelVariables(topLevelVariables); extractStatementComments(thenBranch, ifBranch); - ifBranch.setStatement(thenBranch); for (final PsiExpression label : labels) { if (label instanceof PsiReferenceExpression) { final PsiReferenceExpression reference = @@ -118,26 +107,18 @@ public class ReplaceIfWithSwitchIntention extends Intention { } } branches.add(ifBranch); - final PsiStatement elseBranch = ifStatement.getElseBranch(); + final PsiStatement elseBranch = ifStatement.getElseBranch(); if (elseBranch instanceof PsiIfStatement) { ifStatement = (PsiIfStatement)elseBranch; } else if (elseBranch == null) { break; } else { - final Set elseTopLevelVariables = new HashSet(5); - final Set elseInnerVariables = new HashSet(5); - DeclarationUtils.calculateVariablesDeclared( - elseBranch, elseTopLevelVariables, elseInnerVariables, - true); - final IfStatementBranch elseIfBranch = new IfStatementBranch(); + final IfStatementBranch elseIfBranch = + new IfStatementBranch(elseBranch, true); final PsiKeyword elseKeyword = ifStatement.getElseElement(); extractIfComments(elseKeyword, elseIfBranch); extractStatementComments(elseBranch, elseIfBranch); - elseIfBranch.setInnerVariables(elseInnerVariables); - elseIfBranch.setTopLevelVariables(elseTopLevelVariables); - elseIfBranch.setElse(); - elseIfBranch.setStatement(elseBranch); branches.add(elseIfBranch); break; } @@ -152,7 +133,10 @@ public class ReplaceIfWithSwitchIntention extends Intention { for (IfStatementBranch branch : branches) { boolean hasConflicts = false; for (IfStatementBranch testBranch : branches) { - if (branch.topLevelDeclarationsConfictWith(testBranch)) { + if (branch == testBranch) { + continue; + } + if (branch.topLevelDeclarationsConflictWith(testBranch)) { hasConflicts = true; } } @@ -164,8 +148,7 @@ public class ReplaceIfWithSwitchIntention extends Intention { branch.getStatementComments(); dumpDefaultBranch(switchStatementText, comments, branchStatement, statementComments, - hasConflicts, - breaksNeedRelabeled, labelString); + hasConflicts, breaksNeedRelabeled, labelString); } else { final List conditions = branch.getConditions(); final List comments = branch.getComments(); @@ -383,34 +366,29 @@ public class ReplaceIfWithSwitchIntention extends Intention { private static void dumpBody(@NonNls StringBuilder switchStatementText, PsiStatement bodyStatement, boolean wrap, boolean renameBreaks, String breakLabelName) { + if (wrap) { + switchStatementText.append('{'); + } if (bodyStatement instanceof PsiBlockStatement) { - if (wrap) { - appendElement(switchStatementText, bodyStatement, - renameBreaks, breakLabelName); - } else { - final PsiCodeBlock codeBlock = - ((PsiBlockStatement)bodyStatement).getCodeBlock(); - final PsiElement[] children = codeBlock.getChildren(); - //skip the first and last members, to unwrap the block - for (int i = 1; i < children.length - 1; i++) { - final PsiElement child = children[i]; - appendElement(switchStatementText, child, renameBreaks, - breakLabelName); - } + final PsiCodeBlock codeBlock = + ((PsiBlockStatement)bodyStatement).getCodeBlock(); + final PsiElement[] children = codeBlock.getChildren(); + //skip the first and last members, to unwrap the block + for (int i = 1; i < children.length - 1; i++) { + final PsiElement child = children[i]; + appendElement(switchStatementText, child, renameBreaks, + breakLabelName); } } else { - if (wrap) { - switchStatementText.append('{'); - appendElement(switchStatementText, bodyStatement, - renameBreaks, breakLabelName); - switchStatementText.append('}'); - } else { - appendElement(switchStatementText, bodyStatement, - renameBreaks, breakLabelName); - } + appendElement(switchStatementText, bodyStatement, + renameBreaks, breakLabelName); } - if (ControlFlowUtils.statementMayCompleteNormally(bodyStatement)) { - switchStatementText.append("break; "); + if (ControlFlowUtils.statementMayCompleteNormally( + bodyStatement)) { + switchStatementText.append("break;"); + } + if (wrap) { + switchStatementText.append('}'); } } From 274220cb877bb437014101e2b361aa5781a1b027 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 18 Feb 2011 20:51:41 +0100 Subject: [PATCH 19/38] fix test data-3? (live with the incorrect tree update) --- .../com/intellij/projectView/NavigateFromSourceTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java b/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java index 2e5167fbee58..6e836aaed54c 100644 --- a/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java +++ b/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java @@ -83,7 +83,8 @@ public class NavigateFromSourceTest extends BaseProjectViewTestCase { " -PsiDirectory: src\n" + " -PsiDirectory: com\n" + " -PsiDirectory: package1\n" + - " [Class1]\n" + + " -Class1.java\n" + + " [Class1]\n" + " Class2\n" + getRootFiles() + " +External Libraries\n"); From e727383b87d871c33523810cc33265278f0e186e Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 18 Feb 2011 21:14:50 +0100 Subject: [PATCH 20/38] cleanup --- .../siyeh/ipp/psiutils/ControlFlowUtils.java | 18 +++++++++++------- .../ipp/switchtoif/IfToSwitchPredicate.java | 2 +- .../ReplaceIfWithSwitchIntention.java | 14 +++++++------- .../ReplaceSwitchWithIfIntention.java | 6 +++--- .../{CaseUtil.java => SwitchUtils.java} | 6 ++---- 5 files changed, 24 insertions(+), 22 deletions(-) rename plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/{CaseUtil.java => SwitchUtils.java} (99%) diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ControlFlowUtils.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ControlFlowUtils.java index a2daeea7182a..16848a9d3584 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ControlFlowUtils.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ControlFlowUtils.java @@ -171,16 +171,17 @@ public class ControlFlowUtils{ return breakFinder.breakFound(); } - public static boolean statementContainsExitingBreak(PsiStatement statement){ + public static boolean statementContainsNakedBreak(PsiStatement statement){ if(statement == null){ return false; } - final ExitingBreakFinder breakFinder = new ExitingBreakFinder(); + final NakedBreakFinder breakFinder = new NakedBreakFinder(); statement.accept(breakFinder); return breakFinder.breakFound(); } - private static class BreakTargetFinder extends JavaRecursiveElementWalkingVisitor{ + private static class BreakTargetFinder + extends JavaRecursiveElementWalkingVisitor{ private boolean m_found = false; private final PsiStatement m_target; @@ -218,7 +219,7 @@ public class ControlFlowUtils{ } } - private static class ExitingBreakFinder + private static class NakedBreakFinder extends JavaRecursiveElementWalkingVisitor{ private boolean m_found = false; @@ -235,7 +236,8 @@ public class ControlFlowUtils{ super.visitElement(element); } - @Override public void visitReferenceExpression(PsiReferenceExpression expression){ + @Override public void visitReferenceExpression( + PsiReferenceExpression expression){ } @Override public void visitBreakStatement(PsiBreakStatement statement){ @@ -245,7 +247,8 @@ public class ControlFlowUtils{ m_found = true; } - @Override public void visitDoWhileStatement(PsiDoWhileStatement statement){ + @Override public void visitDoWhileStatement( + PsiDoWhileStatement statement){ // don't drill down } @@ -262,7 +265,8 @@ public class ControlFlowUtils{ // don't drill down } - @Override public void visitSwitchStatement(PsiSwitchStatement statement){ + @Override public void visitSwitchStatement( + PsiSwitchStatement statement){ // don't drill down } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfToSwitchPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfToSwitchPredicate.java index 74c28f0e3e81..d0698ada3846 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfToSwitchPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfToSwitchPredicate.java @@ -40,6 +40,6 @@ class IfToSwitchPredicate implements PsiElementPredicate{ if(ErrorUtil.containsError(statement)){ return false; } - return CaseUtil.getSwitchExpression(statement) != null; + return SwitchUtils.getSwitchExpression(statement) != null; } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java index fe3c56ec6933..add5b6fd8878 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java @@ -49,7 +49,7 @@ public class ReplaceIfWithSwitchIntention extends Intention { boolean breaksNeedRelabeled = false; PsiStatement breakTarget = null; String labelString = ""; - if (ControlFlowUtils.statementContainsExitingBreak(ifStatement)) { + if (ControlFlowUtils.statementContainsNakedBreak(ifStatement)) { breakTarget = PsiTreeUtil.getParentOfType(ifStatement, PsiLoopStatement.class, PsiSwitchStatement.class); if (breakTarget != null) { @@ -62,23 +62,23 @@ public class ReplaceIfWithSwitchIntention extends Intention { breakTarget = labeledStatement; breaksNeedRelabeled = true; } else { - labelString = CaseUtil.findUniqueLabelName(ifStatement, + labelString = SwitchUtils.findUniqueLabelName(ifStatement, "label"); breaksNeedRelabeled = true; } } } final PsiIfStatement statementToReplace = ifStatement; - final PsiExpression caseExpression = - CaseUtil.getSwitchExpression(ifStatement); - assert caseExpression != null; + final PsiExpression switchExpression = + SwitchUtils.getSwitchExpression(ifStatement); + assert switchExpression != null; final List branches = new ArrayList(20); while (true) { final PsiExpression condition = ifStatement.getCondition(); final List labels = - getValuesFromExpression(condition, caseExpression, + getValuesFromExpression(condition, switchExpression, new ArrayList()); final PsiStatement thenBranch = ifStatement.getThenBranch(); final IfStatementBranch ifBranch = @@ -127,7 +127,7 @@ public class ReplaceIfWithSwitchIntention extends Intention { @NonNls final StringBuilder switchStatementText = new StringBuilder(); switchStatementText.append("switch("); - switchStatementText.append(caseExpression.getText()); + switchStatementText.append(switchExpression.getText()); switchStatementText.append(')'); switchStatementText.append('{'); for (IfStatementBranch branch : branches) { diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java index baf4206c6ae8..a7ac58119dad 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java @@ -92,7 +92,7 @@ public class ReplaceSwitchWithIfIntention extends Intention { final PsiStatement[] statements = body.getStatements(); boolean renameBreaks = false; for (int i = 1; i < statements.length - 1; i++) { - if (ControlFlowUtils.statementContainsExitingBreak(statements[i])) { + if (ControlFlowUtils.statementContainsNakedBreak(statements[i])) { renameBreaks = true; break; } @@ -164,7 +164,7 @@ public class ReplaceSwitchWithIfIntention extends Intention { String breakLabel = null; if (renameBreaks) { breakLabel = - CaseUtil.findUniqueLabelName(switchStatement, "label"); + SwitchUtils.findUniqueLabelName(switchStatement, "label"); ifStatementText.append(breakLabel); ifStatementText.append(':'); } @@ -298,7 +298,7 @@ public class ReplaceSwitchWithIfIntention extends Intention { @NonNls StringBuilder ifStatementString) { ifStatementString.append('{'); for (PsiLocalVariable variable : variables) { - if (CaseUtil.isUsedByStatementList(variable, bodyStatements)) { + if (SwitchUtils.isUsedByStatementList(variable, bodyStatements)) { final PsiType varType = variable.getType(); ifStatementString.append(varType.getPresentableText()); ifStatementString.append(' '); diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/CaseUtil.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/SwitchUtils.java similarity index 99% rename from plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/CaseUtil.java rename to plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/SwitchUtils.java index 0e1131dcfb46..a9fb0aaff79d 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/CaseUtil.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/SwitchUtils.java @@ -27,11 +27,9 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -class CaseUtil{ +class SwitchUtils { - private CaseUtil(){ - super(); - } + private SwitchUtils(){} private static boolean canBeCaseLabel(PsiExpression expression, LanguageLevel languageLevel){ From 0b0be5b3978dc08d9603867783af756b00b999fd Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 18 Feb 2011 21:16:49 +0100 Subject: [PATCH 21/38] untested new ""'if' replaceable with 'switch'"" inspection with quickfix that (replaces to string switch under jdk1.7) --- .../com/siyeh/ig/InspectionGadgetsPlugin.java | 1 + .../controlflow/IfCanBeSwitchInspection.java | 483 ++++++++++++++++++ .../ig/controlflow/IfStatementBranch.java | 108 ++++ .../siyeh/ig/psiutils/ControlFlowUtils.java | 88 +++- .../com/siyeh/ig/psiutils/SwitchUtils.java | 250 ++++++++- .../inspectionDescriptions/IfCanBeSwitch.html | 10 + 6 files changed, 925 insertions(+), 15 deletions(-) create mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java create mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfStatementBranch.java create mode 100644 plugins/InspectionGadgets/src/inspectionDescriptions/IfCanBeSwitch.html diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java b/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java index dea51ebec133..5c6a5b9a75b1 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java @@ -481,6 +481,7 @@ public class InspectionGadgetsPlugin implements ApplicationComponent, m_inspectionClasses.add(FallthruInSwitchStatementInspection.class); m_inspectionClasses.add(ForLoopReplaceableByWhileInspection.class); m_inspectionClasses.add(ForLoopWithMissingComponentInspection.class); + m_inspectionClasses.add(IfCanBeSwitchInspection.class); m_inspectionClasses.add(IfMayBeConditionalInspection.class); m_inspectionClasses.add(IfStatementWithIdenticalBranchesInspection.class); m_inspectionClasses.add(IfStatementWithTooManyBranchesInspection.class); diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java new file mode 100644 index 000000000000..26f7826943d1 --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java @@ -0,0 +1,483 @@ +/* + * Copyright 2011 Bas Leijdekkers + * + * 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.siyeh.ig.controlflow; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ControlFlowUtils; +import com.siyeh.ig.psiutils.EquivalenceChecker; +import com.siyeh.ig.psiutils.SwitchUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class IfCanBeSwitchInspection extends BaseInspection { + + @Nls + @NotNull + @Override + public String getDisplayName() { + return "'if' replaceable with 'switch'"; + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return "#ref statement replaceable with 'switch' statement"; + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new IfCanBeSwitchFix((PsiExpression) infos[0]); + } + + private static class IfCanBeSwitchFix extends InspectionGadgetsFix { + + private final PsiExpression switchExpression; + + public IfCanBeSwitchFix(PsiExpression switchExpression) { + this.switchExpression = switchExpression; + } + + @NotNull + public String getName() { + return "Replace with 'switch'"; + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) + throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + if (!(element instanceof PsiIfStatement)) { + return; + } + PsiIfStatement ifStatement = (PsiIfStatement)element; + boolean breaksNeedRelabeled = false; + PsiStatement breakTarget = null; + String labelString = ""; + if (ControlFlowUtils.statementContainsNakedBreak(ifStatement)) { + breakTarget = PsiTreeUtil.getParentOfType(ifStatement, + PsiLoopStatement.class, PsiSwitchStatement.class); + if (breakTarget != null) { + final PsiElement parent = breakTarget.getParent(); + if (parent instanceof PsiLabeledStatement) { + final PsiLabeledStatement labeledStatement = + (PsiLabeledStatement) parent; + labelString = + labeledStatement.getLabelIdentifier().getText(); + breakTarget = labeledStatement; + breaksNeedRelabeled = true; + } else { + labelString = + SwitchUtils.findUniqueLabelName(ifStatement, + "label"); + breaksNeedRelabeled = true; + } + } + } + final PsiIfStatement statementToReplace = ifStatement; + + final List branches = + new ArrayList(20); + while (true) { + final PsiExpression condition = ifStatement.getCondition(); + final List labels = + getValuesFromExpression(condition, switchExpression, + new ArrayList()); + final PsiStatement thenBranch = ifStatement.getThenBranch(); + final IfStatementBranch ifBranch = + new IfStatementBranch(thenBranch, false); + if (!branches.isEmpty()) { + extractIfComments(ifStatement, ifBranch); + } + extractStatementComments(thenBranch, ifBranch); + for (final PsiExpression label : labels) { + if (label instanceof PsiReferenceExpression) { + final PsiReferenceExpression reference = + (PsiReferenceExpression)label; + final PsiElement referent = reference.resolve(); + if (referent instanceof PsiEnumConstant) { + final PsiEnumConstant constant = + (PsiEnumConstant)referent; + final String constantName = constant.getName(); + ifBranch.addCondition(constantName); + } else { + final String labelText = label.getText(); + ifBranch.addCondition(labelText); + } + } else { + final String labelText = label.getText(); + ifBranch.addCondition(labelText); + } + } + branches.add(ifBranch); + final PsiStatement elseBranch = ifStatement.getElseBranch(); + if (elseBranch instanceof PsiIfStatement) { + ifStatement = (PsiIfStatement)elseBranch; + } else if (elseBranch == null) { + break; + } else { + final IfStatementBranch elseIfBranch = + new IfStatementBranch(elseBranch, true); + final PsiKeyword elseKeyword = ifStatement.getElseElement(); + extractIfComments(elseKeyword, elseIfBranch); + extractStatementComments(elseBranch, elseIfBranch); + branches.add(elseIfBranch); + break; + } + } + + @NonNls final StringBuilder switchStatementText = + new StringBuilder(); + switchStatementText.append("switch("); + switchStatementText.append(switchExpression.getText()); + switchStatementText.append(')'); + switchStatementText.append('{'); + for (IfStatementBranch branch : branches) { + boolean hasConflicts = false; + for (IfStatementBranch testBranch : branches) { + if (branch == testBranch) { + continue; + } + if (branch.topLevelDeclarationsConflictWith(testBranch)) { + hasConflicts = true; + } + } + + final PsiStatement branchStatement = branch.getStatement(); + if (branch.isElse()) { + final List comments = branch.getComments(); + final List statementComments = + branch.getStatementComments(); + dumpDefaultBranch(switchStatementText, comments, + branchStatement, statementComments, + hasConflicts, breaksNeedRelabeled, labelString); + } else { + final List conditions = branch.getConditions(); + final List comments = branch.getComments(); + final List statementComments = + branch.getStatementComments(); + dumpBranch(switchStatementText, + comments, conditions, statementComments, + branchStatement, hasConflicts, breaksNeedRelabeled, + labelString); + } + } + switchStatementText.append('}'); + final JavaPsiFacade psiFacade = + JavaPsiFacade.getInstance(element.getProject()); + final PsiElementFactory factory = psiFacade.getElementFactory(); + if (breaksNeedRelabeled) { + final StringBuilder out = new StringBuilder(); + if (!(breakTarget instanceof PsiLabeledStatement)) { + out.append(labelString); + out.append(':'); + } + termReplace(out, breakTarget, statementToReplace, + switchStatementText); + final String newStatementText = out.toString(); + final PsiStatement newStatement = + factory.createStatementFromText(newStatementText, + element); + breakTarget.replace(newStatement); + } else { + final PsiStatement newStatement = + factory.createStatementFromText( + switchStatementText.toString(), element); + statementToReplace.replace(newStatement); + } + + } + + @Nullable + public static T getPrevSiblingOfType( + @Nullable PsiElement element, + @NotNull Class aClass, + @NotNull Class... stopAt) { + if (element == null) { + return null; + } + PsiElement sibling = element.getPrevSibling(); + while (sibling != null && !aClass.isInstance(sibling)) { + for (Class stopClass : stopAt) { + if (stopClass.isInstance(sibling)) { + return null; + } + } + sibling = sibling.getPrevSibling(); + } + return (T)sibling; + } + + private static void extractIfComments(PsiElement element, + IfStatementBranch out) { + PsiComment comment = getPrevSiblingOfType(element, + PsiComment.class, PsiStatement.class); + while (comment != null) { + final PsiElement sibling = comment.getPrevSibling(); + final String commentText; + if (sibling instanceof PsiWhiteSpace) { + final String whiteSpaceText = sibling.getText(); + if (whiteSpaceText.startsWith("\n")) { + commentText = whiteSpaceText.substring(1) + + comment.getText(); + } else { + commentText = comment.getText(); + } + } else { + commentText = comment.getText(); + } + out.addComment(commentText); + comment = getPrevSiblingOfType(comment, PsiComment.class, + PsiStatement.class); + } + } + + private static void extractStatementComments(PsiElement element, + IfStatementBranch out) { + PsiComment comment = getPrevSiblingOfType(element, + PsiComment.class, PsiStatement.class, PsiKeyword.class); + while (comment != null) { + final PsiElement sibling = comment.getPrevSibling(); + final String commentText; + if (sibling instanceof PsiWhiteSpace) { + final String whiteSpaceText = sibling.getText(); + if (whiteSpaceText.startsWith("\n")) { + commentText = whiteSpaceText.substring(1) + + comment.getText(); + } else { + commentText = comment.getText(); + } + } else { + commentText = comment.getText(); + } + out.addStatementComment(commentText); + comment = getPrevSiblingOfType(comment, PsiComment.class, + PsiStatement.class, PsiKeyword.class); + } + } + + private static void termReplace( + StringBuilder out, PsiElement target, + PsiElement replace, StringBuilder stringToReplaceWith) { + if (target.equals(replace)) { + out.append(stringToReplaceWith); + } else if (target.getChildren().length == 0) { + out.append(target.getText()); + } else { + final PsiElement[] children = target.getChildren(); + for (final PsiElement child : children) { + termReplace(out, child, replace, stringToReplaceWith); + } + } + } + + private static List getValuesFromExpression( + PsiExpression expression, PsiExpression caseExpression, + List values) { + if (expression instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = + (PsiMethodCallExpression) expression; + final PsiExpressionList argumentList = + methodCallExpression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + final PsiExpression argument = arguments[0]; + final PsiReferenceExpression methodExpression = + methodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = + methodExpression.getQualifierExpression(); + if (EquivalenceChecker.expressionsAreEquivalent(caseExpression, + argument)) { + values.add(qualifierExpression); + } else { + values.add(argument); + } + } else if (expression instanceof PsiBinaryExpression) { + final PsiBinaryExpression binaryExpression = + (PsiBinaryExpression)expression; + final PsiExpression lhs = binaryExpression.getLOperand(); + final PsiExpression rhs = binaryExpression.getROperand(); + final PsiJavaToken sign = binaryExpression.getOperationSign(); + final IElementType tokenType = sign.getTokenType(); + if (JavaTokenType.OROR.equals(tokenType)) { + getValuesFromExpression(lhs, caseExpression, + values); + getValuesFromExpression(rhs, caseExpression, + values); + } else { + if (EquivalenceChecker.expressionsAreEquivalent( + caseExpression, rhs)) { + values.add(lhs); + } else { + values.add(rhs); + } + } + } else if (expression instanceof PsiParenthesizedExpression) { + final PsiParenthesizedExpression parenthesizedExpression = + (PsiParenthesizedExpression)expression; + final PsiExpression contents = + parenthesizedExpression.getExpression(); + getValuesFromExpression(contents, caseExpression, values); + } + return values; + } + + private static void dumpBranch(StringBuilder switchStatementText, + List comments, + List labels, + List statementComments, + PsiStatement body, + boolean wrap, boolean renameBreaks, + String breakLabelName) { + dumpComments(switchStatementText, comments); + dumpLabels(switchStatementText, labels); + dumpComments(switchStatementText, statementComments); + dumpBody(switchStatementText, body, wrap, renameBreaks, + breakLabelName); + } + + private static void dumpComments(StringBuilder switchStatementText, + List comments) { + if (!comments.isEmpty()) { + switchStatementText.append('\n'); + for (String comment : comments) { + switchStatementText.append(comment); + switchStatementText.append('\n'); + } + } + } + + private static void dumpDefaultBranch( + @NonNls StringBuilder switchStatementString, + List comments, PsiStatement body, + List statementComments, boolean wrap, + boolean renameBreaks, String breakLabelName) { + dumpComments(switchStatementString, comments); + switchStatementString.append("default: "); + dumpComments(switchStatementString, statementComments); + dumpBody(switchStatementString, body, wrap, renameBreaks, + breakLabelName); + } + + private static void dumpLabels( + @NonNls StringBuilder switchStatementText, + List labels) { + for (String label : labels) { + switchStatementText.append("case "); + switchStatementText.append(label); + switchStatementText.append(": "); + } + } + + private static void dumpBody( + @NonNls StringBuilder switchStatementText, + PsiStatement bodyStatement, boolean wrap, + boolean renameBreaks, String breakLabelName) { + if (wrap) { + switchStatementText.append('{'); + } + if (bodyStatement instanceof PsiBlockStatement) { + final PsiCodeBlock codeBlock = + ((PsiBlockStatement)bodyStatement).getCodeBlock(); + final PsiElement[] children = codeBlock.getChildren(); + //skip the first and last members, to unwrap the block + for (int i = 1; i < children.length - 1; i++) { + final PsiElement child = children[i]; + appendElement(switchStatementText, child, renameBreaks, + breakLabelName); + } + } else { + appendElement(switchStatementText, bodyStatement, + renameBreaks, breakLabelName); + } + if (ControlFlowUtils.statementMayCompleteNormally( + bodyStatement)) { + switchStatementText.append("break;"); + } + if (wrap) { + switchStatementText.append('}'); + } + } + + private static void appendElement( + @NonNls StringBuilder switchStatementText, + PsiElement element, boolean renameBreakElements, + String breakLabelString) { + final String text = element.getText(); + if (!renameBreakElements) { + switchStatementText.append(text); + } else if (element instanceof PsiBreakStatement) { + final PsiIdentifier identifier = + ((PsiBreakStatement)element).getLabelIdentifier(); + if (identifier == null) { + switchStatementText.append("break "); + switchStatementText.append(breakLabelString); + switchStatementText.append(';'); + } else { + final String identifierText = identifier.getText(); + if ("".equals(identifierText)) { + switchStatementText.append("break "); + switchStatementText.append(breakLabelString); + switchStatementText.append(';'); + } else { + switchStatementText.append(text); + } + } + } else if (element instanceof PsiBlockStatement || + element instanceof PsiCodeBlock || + element instanceof PsiIfStatement) { + final PsiElement[] children = element.getChildren(); + for (final PsiElement child : children) { + appendElement(switchStatementText, child, + renameBreakElements, breakLabelString); + } + } else { + switchStatementText.append(text); + } + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new IfCanBeSwitchVisitor(); + } + + private static class IfCanBeSwitchVisitor extends BaseInspectionVisitor { + + @Override + public void visitIfStatement(PsiIfStatement statement) { + super.visitIfStatement(statement); + final PsiExpression switchExpression = + SwitchUtils.getSwitchExpression(statement); + if (switchExpression == null) { + return; + } + registerStatementError(statement, switchExpression); + } + } + +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfStatementBranch.java b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfStatementBranch.java new file mode 100644 index 000000000000..582ee9b4755e --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfStatementBranch.java @@ -0,0 +1,108 @@ +/* + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * + * 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.siyeh.ig.controlflow; + +import com.intellij.psi.*; + +import java.util.*; + +class IfStatementBranch{ + + private final Set topLevelVariables = new HashSet(3); + private final LinkedList comments = new LinkedList(); + private final LinkedList statementComments = new LinkedList(); + private final List conditions = new ArrayList(3); + private final PsiStatement statement; + private final boolean elseBranch; + + public IfStatementBranch(PsiStatement branch, boolean elseBranch) { + statement = branch; + this.elseBranch = elseBranch; + calculateVariablesDeclared(statement); + } + + public void addComment(String comment){ + comments.addFirst(comment); + } + + public void addStatementComment(String comment) { + statementComments.addFirst(comment); + } + + public void addCondition(String conditionString){ + conditions.add(conditionString); + } + + public PsiStatement getStatement(){ + return statement; + } + + public List getConditions(){ + return Collections.unmodifiableList(conditions); + } + + public boolean isElse(){ + return elseBranch; + } + + public boolean topLevelDeclarationsConflictWith( + IfStatementBranch testBranch){ + final Set topLevel = testBranch.topLevelVariables; + return intersects(topLevelVariables, topLevel); + } + + private static boolean intersects(Set set1, + Set set2){ + for(final String s : set1){ + if(set2.contains(s)){ + return true; + } + } + return false; + } + + public List getComments() { + return comments; + } + + public List getStatementComments() { + return statementComments; + } + + public void calculateVariablesDeclared(PsiStatement statement){ + if(statement == null){ + return; + } + if(statement instanceof PsiDeclarationStatement){ + final PsiDeclarationStatement declarationStatement = + (PsiDeclarationStatement) statement; + final PsiElement[] elements = + declarationStatement.getDeclaredElements(); + for(PsiElement element : elements){ + final PsiVariable variable = (PsiVariable) element; + final String varName = variable.getName(); + topLevelVariables.add(varName); + } + } else if(statement instanceof PsiBlockStatement){ + final PsiBlockStatement block = (PsiBlockStatement) statement; + final PsiCodeBlock codeBlock = block.getCodeBlock(); + final PsiStatement[] statements = codeBlock.getStatements(); + for(PsiStatement statement1 : statements){ + calculateVariablesDeclared(statement1); + } + } + } +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ControlFlowUtils.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ControlFlowUtils.java index c26c40aa2763..d84e25213043 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ControlFlowUtils.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ControlFlowUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,9 +23,7 @@ import org.jetbrains.annotations.Nullable; public class ControlFlowUtils{ - private ControlFlowUtils(){ - super(); - } + private ControlFlowUtils(){} public static boolean statementMayCompleteNormally( @Nullable PsiStatement statement){ @@ -503,6 +501,67 @@ public class ControlFlowUtils{ return !codeBlockMayCompleteNormally(body); } + public static boolean statementContainsNakedBreak(PsiStatement statement){ + if(statement == null){ + return false; + } + final NakedBreakFinder breakFinder = new NakedBreakFinder(); + statement.accept(breakFinder); + return breakFinder.breakFound(); + } + + private static class NakedBreakFinder + extends JavaRecursiveElementWalkingVisitor{ + + private boolean m_found = false; + + public boolean breakFound(){ + return m_found; + } + + @Override + public void visitElement(PsiElement element) { + if (m_found) { + return; + } + super.visitElement(element); + } + + @Override public void visitReferenceExpression( + PsiReferenceExpression expression){ + } + + @Override public void visitBreakStatement(PsiBreakStatement statement){ + if(statement.getLabelIdentifier() != null){ + return; + } + m_found = true; + } + + @Override public void visitDoWhileStatement( + PsiDoWhileStatement statement){ + // don't drill down + } + + @Override public void visitForStatement(PsiForStatement statement){ + // don't drill down + } + + @Override + public void visitForeachStatement(PsiForeachStatement statement) { + // don't drill down + } + + @Override public void visitWhileStatement(PsiWhileStatement statement){ + // don't drill down + } + + @Override public void visitSwitchStatement( + PsiSwitchStatement statement){ + // don't drill down + } + } + private static class SystemExitFinder extends JavaRecursiveElementVisitor{ private boolean m_found = false; @@ -599,7 +658,6 @@ public class ControlFlowUtils{ private String m_label = null; private ContinueFinder(@NotNull PsiStatement target){ - super(); if(target.getParent() instanceof PsiLabeledStatement){ final PsiLabeledStatement labeledStatement = (PsiLabeledStatement) target.getParent(); @@ -694,8 +752,12 @@ public class ControlFlowUtils{ this.parameterTypeNames = parameterTypeNames; } - public boolean containsCallToMethod() { - return containsCallToMethod; + @Override + public void visitElement(PsiElement element) { + if (containsCallToMethod) { + return; + } + super.visitElement(element); } @Override public void visitMethodCallExpression( @@ -710,6 +772,10 @@ public class ControlFlowUtils{ } containsCallToMethod = true; } + + public boolean containsCallToMethod() { + return containsCallToMethod; + } } private static class ContinueToAncestorFinder @@ -722,6 +788,14 @@ public class ControlFlowUtils{ this.statement = statement; } + @Override + public void visitElement(PsiElement element) { + if (found) { + return; + } + super.visitElement(element); + } + @Override public void visitContinueStatement( PsiContinueStatement continueStatement) { diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/SwitchUtils.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/SwitchUtils.java index 5564882b4166..34927faa3472 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/SwitchUtils.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/SwitchUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2006 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,17 +15,17 @@ */ package com.siyeh.ig.psiutils; -import com.intellij.psi.PsiCodeBlock; -import com.intellij.psi.PsiStatement; -import com.intellij.psi.PsiSwitchLabelStatement; -import com.intellij.psi.PsiSwitchStatement; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class SwitchUtils{ - private SwitchUtils(){ - super(); - } + private SwitchUtils(){} public static int calculateBranchCount( @NotNull PsiSwitchStatement statement){ @@ -42,4 +42,238 @@ public class SwitchUtils{ } return branches; } + + @Nullable + public static PsiExpression getSwitchExpression(PsiIfStatement statement){ + final PsiExpression condition = statement.getCondition(); + final LanguageLevel languageLevel = + PsiUtil.getLanguageLevel(statement); + final PsiExpression possibleSwitchExpression = + determinePossibleSwitchExpressions(condition, languageLevel); + if(possibleSwitchExpression == null){ + return null; + } + if (SideEffectChecker.mayHaveSideEffects(possibleSwitchExpression)) { + return null; + } + while(true){ + final PsiExpression caseCondition = statement.getCondition(); + if (!canBeMadeIntoCase(caseCondition, possibleSwitchExpression, + languageLevel)) { + break; + } + final PsiStatement elseBranch = statement.getElseBranch(); + if(!(elseBranch instanceof PsiIfStatement)){ + return possibleSwitchExpression; + } + statement = (PsiIfStatement) elseBranch; + } + return null; + } + + private static boolean canBeMadeIntoCase( + PsiExpression expression, PsiExpression caseExpression, + LanguageLevel languageLevel) { + while(expression instanceof PsiParenthesizedExpression){ + final PsiParenthesizedExpression parenthesizedExpression = + (PsiParenthesizedExpression)expression; + expression = parenthesizedExpression.getExpression(); + } + if (languageLevel.compareTo(LanguageLevel.JDK_1_7) >=0 ) { + final PsiExpression stringCaseExpression = + determinePossibleStringSwitchExpression(expression); + if (EquivalenceChecker.expressionsAreEquivalent(caseExpression, + stringCaseExpression)) { + return true; + } + } + if(!(expression instanceof PsiBinaryExpression)){ + return false; + } + final PsiBinaryExpression binaryExpression = + (PsiBinaryExpression) expression; + final PsiJavaToken sign = binaryExpression.getOperationSign(); + final IElementType operation = sign.getTokenType(); + final PsiExpression lOperand = binaryExpression.getLOperand(); + final PsiExpression rhs = binaryExpression.getROperand(); + if(operation.equals(JavaTokenType.OROR)){ + return canBeMadeIntoCase(lOperand, caseExpression, languageLevel) && + canBeMadeIntoCase(rhs, caseExpression, languageLevel); + } else if(operation.equals(JavaTokenType.EQEQ)){ + return (canBeCaseLabel(lOperand, languageLevel) && + EquivalenceChecker.expressionsAreEquivalent( + caseExpression, rhs)) + || + (canBeCaseLabel(rhs, languageLevel) && + EquivalenceChecker.expressionsAreEquivalent( + caseExpression, lOperand)); + } else { + return false; + } + } + + private static PsiExpression determinePossibleSwitchExpressions( + PsiExpression expression, LanguageLevel languageLevel){ + while(expression instanceof PsiParenthesizedExpression){ + final PsiParenthesizedExpression parenthesizedExpression = + (PsiParenthesizedExpression)expression; + expression = parenthesizedExpression.getExpression(); + } + if (expression == null) { + return null; + } + if (languageLevel.compareTo(LanguageLevel.JDK_1_7) >= 0) { + final PsiExpression jdk17Expression = + determinePossibleStringSwitchExpression(expression); + if (jdk17Expression != null) { + return jdk17Expression; + } + } + if (!(expression instanceof PsiBinaryExpression)){ + return null; + } + final PsiBinaryExpression binaryExpression = + (PsiBinaryExpression) expression; + final PsiJavaToken sign = binaryExpression.getOperationSign(); + final IElementType operation = sign.getTokenType(); + final PsiExpression lhs = binaryExpression.getLOperand(); + final PsiExpression rhs = binaryExpression.getROperand(); + if(operation.equals(JavaTokenType.OROR)){ + return determinePossibleSwitchExpressions(lhs, languageLevel); + } else if(operation.equals(JavaTokenType.EQEQ)){ + if(canBeCaseLabel(lhs, languageLevel)){ + return rhs; + } else if (canBeCaseLabel(rhs, languageLevel)){ + return lhs; + } + } + return null; + } + + private static PsiExpression determinePossibleStringSwitchExpression( + PsiExpression expression) { + if (!(expression instanceof PsiMethodCallExpression)) { + return null; + } + final PsiMethodCallExpression methodCallExpression = + (PsiMethodCallExpression) expression; + final PsiReferenceExpression methodExpression = + methodCallExpression.getMethodExpression(); + @NonNls final String referenceName = + methodExpression.getReferenceName(); + if (!"equals".equals(referenceName)) { + return null; + } + final PsiExpression qualifierExpression = + methodExpression.getQualifierExpression(); + if (qualifierExpression == null) { + return null; + } + final PsiType type = qualifierExpression.getType(); + if (type == null || !type.equalsToText("java.lang.String")) { + return null; + } + final PsiExpressionList argumentList = + methodCallExpression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length != 1) { + return null; + } + final PsiExpression argument = arguments[0]; + final PsiType argumentType = argument.getType(); + if (argumentType == null || + !argumentType.equalsToText("java.lang.String")) { + return null; + } + if (PsiUtil.isConstantExpression(qualifierExpression)) { + return argument; + } else if (PsiUtil.isConstantExpression(argument)) { + return qualifierExpression; + } + return null; + } + + private static boolean canBeCaseLabel(PsiExpression expression, + LanguageLevel languageLevel){ + if(expression == null){ + return false; + } + if (languageLevel.compareTo(LanguageLevel.JDK_1_5) >= 0 + && expression instanceof PsiReferenceExpression){ + final PsiElement referent = ((PsiReference) expression).resolve(); + if(referent instanceof PsiEnumConstant){ + return true; + } + } + final PsiType type = expression.getType(); + return type != null && + (type.equals(PsiType.INT) || + type.equals(PsiType.CHAR) || + type.equals(PsiType.LONG) || + type.equals(PsiType.SHORT)) && + PsiUtil.isConstantExpression(expression); + } + + public static String findUniqueLabelName(PsiStatement statement, + @NonNls String baseName){ + PsiElement ancestor = statement; + while(ancestor.getParent() != null){ + if(ancestor instanceof PsiMethod + || ancestor instanceof PsiClass + || ancestor instanceof PsiFile){ + break; + } + ancestor = ancestor.getParent(); + } + if(!checkForLabel(baseName, ancestor)){ + return baseName; + } + int val = 1; + while(true){ + final String name = baseName + val; + if(!checkForLabel(name, ancestor)){ + return name; + } + val++; + } + } + + private static boolean checkForLabel(String name, PsiElement ancestor){ + final LabelSearchVisitor visitor = new LabelSearchVisitor(name); + ancestor.accept(visitor); + return visitor.isUsed(); + } + + private static class LabelSearchVisitor + extends JavaRecursiveElementWalkingVisitor { + + private final String m_labelName; + private boolean m_used = false; + + LabelSearchVisitor(String name){ + m_labelName = name; + } + + @Override + public void visitElement(PsiElement element) { + if (m_used) { + return; + } + super.visitElement(element); + } + + @Override + public void visitLabeledStatement(PsiLabeledStatement statement){ + final PsiIdentifier labelIdentifier = + statement.getLabelIdentifier(); + final String labelText = labelIdentifier.getText(); + if(labelText.equals(m_labelName)){ + m_used = true; + } + } + + public boolean isUsed(){ + return m_used; + } + } } diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/IfCanBeSwitch.html b/plugins/InspectionGadgets/src/inspectionDescriptions/IfCanBeSwitch.html new file mode 100644 index 000000000000..5130a10fd67f --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/IfCanBeSwitch.html @@ -0,0 +1,10 @@ + + +This inspection reports any if statements with which can be replaced +by a switch statement. This inspection will automatically suggest string +switches when the project language level is jdk 1.7 or higher. +Use the text field below to indicate the minimum +number of case branches the resulting switch statement should have. +New in 11?, Powered by InspectionGadgets + + \ No newline at end of file From 321d048fe896bbc6aee96350e8266246c8c14939 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Sat, 19 Feb 2011 00:39:18 +0300 Subject: [PATCH 22/38] simplify EP interface --- .../featureStatistics/FeatureStatisticsBundle.java | 9 ++++----- .../FeatureStatisticsBundleProvider.java | 5 ++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/platform/platform-api/src/com/intellij/featureStatistics/FeatureStatisticsBundle.java b/platform/platform-api/src/com/intellij/featureStatistics/FeatureStatisticsBundle.java index ec2ea0b7fe58..d2de4d61f752 100644 --- a/platform/platform-api/src/com/intellij/featureStatistics/FeatureStatisticsBundle.java +++ b/platform/platform-api/src/com/intellij/featureStatistics/FeatureStatisticsBundle.java @@ -39,15 +39,14 @@ public class FeatureStatisticsBundle { } private static ResourceBundle getBundle(final String key) { - // bundles provided by plugins - for (FeatureStatisticsBundleProvider provider : FeatureStatisticsBundleProvider.EP_NAME.getExtensions()) { - final ResourceBundle bundle = provider.getBundleFor(key); - if (bundle != null) { + final FeatureStatisticsBundleProvider[] providers = FeatureStatisticsBundleProvider.EP_NAME.getExtensions(); + for (FeatureStatisticsBundleProvider provider : providers) { + final ResourceBundle bundle = provider.getBundle(); + if (bundle.containsKey(key)) { return bundle; } } - // default bundle ResourceBundle bundle = null; if (ourBundle != null) bundle = ourBundle.get(); if (bundle == null) { diff --git a/platform/platform-api/src/com/intellij/featureStatistics/FeatureStatisticsBundleProvider.java b/platform/platform-api/src/com/intellij/featureStatistics/FeatureStatisticsBundleProvider.java index a8e53b1e6fe7..7cea610f4746 100644 --- a/platform/platform-api/src/com/intellij/featureStatistics/FeatureStatisticsBundleProvider.java +++ b/platform/platform-api/src/com/intellij/featureStatistics/FeatureStatisticsBundleProvider.java @@ -16,16 +16,15 @@ package com.intellij.featureStatistics; import com.intellij.openapi.extensions.ExtensionPointName; -import org.jetbrains.annotations.Nullable; import java.util.ResourceBundle; /** + * @author Konstantin Bulenkov * @author Roman.Chernyatchik */ public interface FeatureStatisticsBundleProvider { ExtensionPointName EP_NAME = new ExtensionPointName("com.intellij.featureStatBundle"); - @Nullable - ResourceBundle getBundleFor(String key); + ResourceBundle getBundle(); } From 24216ecfae4a36bc69d14673f88ba2c59dd1d227 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Sat, 19 Feb 2011 00:47:08 +0300 Subject: [PATCH 23/38] IdeSpecificFeatures.xml and IdeSpecificTips.xml for describing ide specific stuff --- .../ProductivityFeaturesRegistryImpl.java | 20 ++++-- .../src/com/intellij/ide/util/TipPanel.java | 64 +++++-------------- 2 files changed, 33 insertions(+), 51 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/featureStatistics/ProductivityFeaturesRegistryImpl.java b/platform/platform-impl/src/com/intellij/featureStatistics/ProductivityFeaturesRegistryImpl.java index fb9e95868b41..b61d3fd82571 100644 --- a/platform/platform-impl/src/com/intellij/featureStatistics/ProductivityFeaturesRegistryImpl.java +++ b/platform/platform-impl/src/com/intellij/featureStatistics/ProductivityFeaturesRegistryImpl.java @@ -22,8 +22,10 @@ import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Pair; import org.jdom.Document; import org.jdom.Element; +import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; +import java.io.IOException; import java.net.URL; import java.util.*; @@ -46,14 +48,24 @@ public class ProductivityFeaturesRegistryImpl extends ProductivityFeaturesRegist public ProductivityFeaturesRegistryImpl() { try { - final Document document = JDOMUtil.loadResourceDocument(new URL("file:///ProductivityFeaturesRegistry.xml")); - final Element root = document.getRootElement(); - readGroups(root); - readFilters(root); + readFromXml("file:///ProductivityFeaturesRegistry.xml"); } catch (Exception e) { LOG.error(e); } + + try { + readFromXml("file:///IdeSpecificFeatures.xml"); + } + catch (Exception e) {// ignore + } + } + + private void readFromXml(String path) throws JDOMException, IOException { + final Document document = JDOMUtil.loadResourceDocument(new URL(path)); + final Element root = document.getRootElement(); + readGroups(root); + readFilters(root); } private void lazyLoadFromPluginsFeaturesProviders() { diff --git a/platform/platform-impl/src/com/intellij/ide/util/TipPanel.java b/platform/platform-impl/src/com/intellij/ide/util/TipPanel.java index 2e99de9e1a55..ea3c0ad501e4 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/TipPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/util/TipPanel.java @@ -19,9 +19,7 @@ import com.intellij.featureStatistics.FeatureDescriptor; import com.intellij.featureStatistics.ProductivityFeaturesProvider; import com.intellij.ide.GeneralSettings; import com.intellij.ide.IdeBundle; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.JDOMUtil; import com.intellij.ui.ScrollPaneFactory; @@ -38,10 +36,8 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; public class TipPanel extends JPanel { - private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.TipPanel"); private static final int DEFAULT_WIDTH = 400; private static final int DEFAULT_HEIGHT = 200; private final JCheckBox myCheckBox; @@ -92,10 +88,10 @@ public class TipPanel extends JPanel { }); add(myCheckBox, BorderLayout.SOUTH); try { - readTips(); + readTips("/tips/tips.xml"); + readTips("/tips/IdeSpecificTips.xml"); } - catch (Exception exception) { - + catch (Exception exception) {// } } @@ -108,10 +104,10 @@ public class TipPanel extends JPanel { browser.setText(IdeBundle.message("error.tips.not.found", ApplicationNamesInfo.getInstance().getFullProductName())); return; } - GeneralSettings settings = GeneralSettings.getInstance(); + final GeneralSettings settings = GeneralSettings.getInstance(); int lastTip = settings.getLastTip(); - String path; + final String path; lastTip--; if (lastTip <= 0) { path = myTipPaths.get(myTipPaths.size() - 1); @@ -126,7 +122,6 @@ public class TipPanel extends JPanel { private void setTip (String path, int lastTip, JEditorPane browser, GeneralSettings settings) { TipUIUtil.openTipInBrowser(path, browser, myPathsToProviderMap.get(path)); - settings.setLastTip(lastTip); } @@ -148,46 +143,21 @@ public class TipPanel extends JPanel { } setTip(path, lastTip, browser, settings); - /* - try { - String appName = ApplicationUtil.getApplicationName(); - String tipsPath = ResourceUtil.getHomePath() + File.separator + "help" + File.separator + appName + File.separator + "tips" + File.separator; - File file = new File(tipsPath + "tip" + lastTip + ".html"); - if (!file.exists()) { - if (lastTip == 1) { - browser.setText("Tips not found. Make sure you installed IntelliJ IDEA correctly."); - return; - } - lastTip = 1; - file = new File(tipsPath + "tip" + lastTip + ".html"); - if (!file.exists()) { - browser.setText("Tips not found. Make sure you installed IntelliJ IDEA correctly."); - return; - } - } - browser.setPage(file.toURL()); - settings.setLastTip(lastTip); - } - catch (IOException ex) { - ex.printStackTrace(); - } - */ } - private void readTips() throws Exception { - @NonNls String tipsURL = "/tips/" + "tips.xml"; - Document document = JDOMUtil.loadDocument(getClass().getResource(tipsURL).openStream()); - if (document == null) return; - for (Iterator iterator = document.getRootElement().getChildren(ELEMENT_TIP).iterator(); iterator.hasNext();) { - Element element = (Element)iterator.next(); - myTipPaths.add(element.getAttributeValue(ATTRIBUTE_FILE)); + private void readTips(String tipsURL) throws Exception { + final Document document = JDOMUtil.loadDocument(getClass().getResource(tipsURL).openStream()); + + for (Object o : document.getRootElement().getChildren(ELEMENT_TIP)) { + Element tip = (Element)o; + myTipPaths.add(tip.getAttributeValue(ATTRIBUTE_FILE)); } - final ProductivityFeaturesProvider[] providers = ApplicationManager.getApplication().getComponents(ProductivityFeaturesProvider.class); - for (ProductivityFeaturesProvider provider : providers) { - final FeatureDescriptor[] featureDescriptors = provider.getFeatureDescriptors(); - for (int j = 0; featureDescriptors != null && j < featureDescriptors.length; j++) { - FeatureDescriptor featureDescriptor = featureDescriptors[j]; - myPathsToProviderMap.put(featureDescriptor.getTipFileName(), featureDescriptor.getProvider()); + + for (ProductivityFeaturesProvider provider : ProductivityFeaturesProvider.EP_NAME.getExtensions()) { + final FeatureDescriptor[] descriptors = provider.getFeatureDescriptors(); + for (int j = 0; descriptors != null && j < descriptors.length; j++) { + FeatureDescriptor descriptor = descriptors[j]; + myPathsToProviderMap.put(descriptor.getTipFileName(), descriptor.getProvider()); } } } From da95e7834b19ab81d6024a8cd96dc8d1ba38411a Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Sat, 19 Feb 2011 02:27:56 +0300 Subject: [PATCH 24/38] shorter --- .../src/messages/FeatureStatisticsBundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-resources-en/src/messages/FeatureStatisticsBundle.properties b/platform/platform-resources-en/src/messages/FeatureStatisticsBundle.properties index c1d1bd08a8e6..cb008d728f92 100644 --- a/platform/platform-resources-en/src/messages/FeatureStatisticsBundle.properties +++ b/platform/platform-resources-en/src/messages/FeatureStatisticsBundle.properties @@ -114,4 +114,4 @@ codeassists.highlight.return=Highlight method exit points codeassists.highlight.throws=Highlight method throws find.recent.search=Recent searches history find.completion=Completion in Find Bar -switcher=Tab and Tool Window Switcher +switcher=Switcher From fde1a229d7da026ca6da74ec9c0e8e990729f412 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Sat, 19 Feb 2011 11:51:01 +0100 Subject: [PATCH 25/38] IDEA-65653 (Inspection: if/else can be converted to switch) --- .../com/siyeh/InspectionGadgetsBundle.properties | 3 +++ .../ig/controlflow/IfCanBeSwitchInspection.java | 14 +++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index f584e79b4ab1..0e7f1fa35ba4 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1847,3 +1847,6 @@ choose.super.class.to.ignore=Choose class ignore.anonymous.inner.classes=Ignore anonymous inner classes try.with.identical.catches.display.name=Identical 'catch' branches in 'try' statement try.with.identical.catches.problem.descriptor=Identical 'catch' branches in 'try' statement #loc +if.can.be.switch.display.name='if' replaceable with 'switch' +if.can.be.switch.problem.descriptor=#ref statement replaceable with 'switch' statement +if.can.be.switch.quickfix=Replace with 'switch' diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java index 26f7826943d1..86e9bc3e6e59 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java @@ -21,6 +21,7 @@ import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; @@ -41,13 +42,14 @@ public class IfCanBeSwitchInspection extends BaseInspection { @NotNull @Override public String getDisplayName() { - return "'if' replaceable with 'switch'"; + return InspectionGadgetsBundle.message("if.can.be.switch.display.name"); } @NotNull @Override protected String buildErrorString(Object... infos) { - return "#ref statement replaceable with 'switch' statement"; + return InspectionGadgetsBundle.message( + "if.can.be.switch.problem.descriptor"); } @Override @@ -65,13 +67,13 @@ public class IfCanBeSwitchInspection extends BaseInspection { @NotNull public String getName() { - return "Replace with 'switch'"; + return InspectionGadgetsBundle.message("if.can.be.switch.quickfix"); } @Override protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { - final PsiElement element = descriptor.getPsiElement(); + final PsiElement element = descriptor.getPsiElement().getParent(); if (!(element instanceof PsiIfStatement)) { return; } @@ -155,8 +157,7 @@ public class IfCanBeSwitchInspection extends BaseInspection { new StringBuilder(); switchStatementText.append("switch("); switchStatementText.append(switchExpression.getText()); - switchStatementText.append(')'); - switchStatementText.append('{'); + switchStatementText.append("){"); for (IfStatementBranch branch : branches) { boolean hasConflicts = false; for (IfStatementBranch testBranch : branches) { @@ -210,7 +211,6 @@ public class IfCanBeSwitchInspection extends BaseInspection { switchStatementText.toString(), element); statementToReplace.replace(newStatement); } - } @Nullable From 6d86f4f4034987101a7e5a77e421e9e067c8c5b6 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Sat, 19 Feb 2011 11:51:34 +0100 Subject: [PATCH 26/38] better switch with if replacement --- .../ReplaceSwitchWithIfIntention.java | 95 +++++++------------ 1 file changed, 32 insertions(+), 63 deletions(-) diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java index a7ac58119dad..53eeb5f1a255 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceSwitchWithIfIntention.java @@ -89,15 +89,6 @@ public class ReplaceSwitchWithIfIntention extends Intention { if (body == null) { return; } - final PsiStatement[] statements = body.getStatements(); - boolean renameBreaks = false; - for (int i = 1; i < statements.length - 1; i++) { - if (ControlFlowUtils.statementContainsNakedBreak(statements[i])) { - renameBreaks = true; - break; - } - } - final List openBranches = new ArrayList(); final Set declaredVariables = @@ -161,13 +152,6 @@ public class ReplaceSwitchWithIfIntention extends Intention { } } final StringBuilder ifStatementText = new StringBuilder(); - String breakLabel = null; - if (renameBreaks) { - breakLabel = - SwitchUtils.findUniqueLabelName(switchStatement, "label"); - ifStatementText.append(breakLabel); - ifStatementText.append(':'); - } boolean firstBranch = true; SwitchStatementBranch defaultBranch = null; for (SwitchStatementBranch branch : allBranches) { @@ -178,8 +162,8 @@ public class ReplaceSwitchWithIfIntention extends Intention { final List bodyElements = branch.getBodyElements(); final Set pendingVariableDeclarations = branch.getPendingVariableDeclarations(); - dumpBranch(expressionText, caseValues, bodyElements, breakLabel, - pendingVariableDeclarations, firstBranch, renameBreaks, + dumpBranch(expressionText, caseValues, bodyElements, + pendingVariableDeclarations, firstBranch, isSwitchOnString, ifStatementText); firstBranch = false; } @@ -189,9 +173,8 @@ public class ReplaceSwitchWithIfIntention extends Intention { defaultBranch.getBodyElements(); final Set pendingVariableDeclarations = defaultBranch.getPendingVariableDeclarations(); - dumpDefaultBranch(bodyElements, breakLabel, - pendingVariableDeclarations, firstBranch, renameBreaks, - ifStatementText); + dumpDefaultBranch(bodyElements, pendingVariableDeclarations, + firstBranch, ifStatementText); } final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); final PsiElementFactory factory = psiFacade.getElementFactory(); @@ -245,28 +228,26 @@ public class ReplaceSwitchWithIfIntention extends Intention { private static void dumpBranch( String expressionText, List caseValues, - List bodyStatements, String breakLabel, + List bodyStatements, Set variables, boolean firstBranch, - boolean renameBreaks, boolean useEquals, + boolean useEquals, @NonNls StringBuilder ifStatementString) { if (!firstBranch) { ifStatementString.append("else "); } dumpCaseValues(expressionText, caseValues, useEquals, ifStatementString); - dumpBody(bodyStatements, breakLabel, variables, renameBreaks, - ifStatementString); + dumpBody(bodyStatements, variables, ifStatementString); } private static void dumpDefaultBranch( - List bodyStatements, String breakLabel, + List bodyStatements, Set variables, boolean firstBranch, - boolean renameBreaks, @NonNls StringBuilder ifStatementString) { + @NonNls StringBuilder ifStatementString) { if (!firstBranch) { ifStatementString.append("else "); } - dumpBody(bodyStatements, breakLabel, variables, renameBreaks, - ifStatementString); + dumpBody(bodyStatements, variables, ifStatementString); } private static void dumpCaseValues( @@ -292,10 +273,9 @@ public class ReplaceSwitchWithIfIntention extends Intention { ifStatementString.append(')'); } - private static void dumpBody( - List bodyStatements, String breakLabel, - Set variables, boolean renameBreaks, - @NonNls StringBuilder ifStatementString) { + private static void dumpBody(List bodyStatements, + Set variables, + @NonNls StringBuilder ifStatementString) { ifStatementString.append('{'); for (PsiLocalVariable variable : variables) { if (SwitchUtils.isUsedByStatementList(variable, bodyStatements)) { @@ -307,44 +287,33 @@ public class ReplaceSwitchWithIfIntention extends Intention { } } for (PsiElement bodyStatement : bodyStatements) { - @NonNls final String text = bodyStatement.getText(); - if (!"break;".equals(text)) { - appendElement(bodyStatement, breakLabel, renameBreaks, - ifStatementString); + if (bodyStatement instanceof PsiBlockStatement) { + final PsiBlockStatement blockStatement = + (PsiBlockStatement) bodyStatement; + final PsiCodeBlock codeBlock = blockStatement.getCodeBlock(); + final PsiStatement[] statements = codeBlock.getStatements(); + for (PsiStatement statement : statements) { + appendElement(statement, ifStatementString); + } + } else { + appendElement(bodyStatement, ifStatementString); } } ifStatementString.append("\n}"); } private static void appendElement( - PsiElement element, String breakLabelString, - boolean renameBreakElements, - @NonNls StringBuilder ifStatementString) { - if (!renameBreakElements) { - final String text = element.getText(); - ifStatementString.append(text); - } else if (element instanceof PsiBreakStatement) { + PsiElement element, @NonNls StringBuilder ifStatementString) { + if (element instanceof PsiBreakStatement) { + final PsiBreakStatement breakStatement = + (PsiBreakStatement) element; final PsiIdentifier identifier = - ((PsiBreakStatement)element).getLabelIdentifier(); - if (identifier == null || "".equals(identifier.getText())) { - ifStatementString.append("break "); - ifStatementString.append(breakLabelString); - ifStatementString.append(';'); - } else { - final String text = element.getText(); - ifStatementString.append(text); + breakStatement.getLabelIdentifier(); + if (identifier == null) { + return; } - } else if (element instanceof PsiBlockStatement || - element instanceof PsiCodeBlock || - element instanceof PsiIfStatement) { - final PsiElement[] children = element.getChildren(); - for (PsiElement child : children) { - appendElement(child, breakLabelString, renameBreakElements, - ifStatementString); - } - } else { - final String text = element.getText(); - ifStatementString.append(text); } + final String text = element.getText(); + ifStatementString.append(text); } } \ No newline at end of file From 406a77798a7fdcc28bcb1bf6f9df0da3fcd61d1c Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Sat, 19 Feb 2011 11:51:54 +0100 Subject: [PATCH 27/38] intention wording --- .../siyeh/IntentionPowerPackBundle.properties | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties b/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties index ac0896aa51c6..efb61ee190fa 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties +++ b/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties @@ -1,37 +1,37 @@ plugin.IntentionPowerPack.description=Adds over 30 new intention actions for IDEA. -replace.fully.qualified.name.with.import.intention.name=Replace qualified name with import +replace.fully.qualified.name.with.import.intention.name=Replace qualified name with 'import' replace.fully.qualified.name.with.import.intention.family.name=Replace Qualified Name with Import -replace.if.with.switch.intention.name=Replace if with switch +replace.if.with.switch.intention.name=Replace 'if' with 'switch' replace.if.with.switch.intention.family.name=Replace If with Switch merge.parallel.ifs.intention.name=Merge 'if's merge.parallel.ifs.intention.family.name=Merge Parallel Ifs -replace.conditional.with.if.intention.name=Replace ?: with if-else +replace.conditional.with.if.intention.name=Replace '?:' with 'if else' replace.conditional.with.if.intention.family.name=Replace Conditional with If Else -merge.else.if.intention.name=Merge else-if +merge.else.if.intention.name=Merge 'else if' merge.else.if.intention.family.name=Merge Else If change.to.end.of.line.comment.intention.name=Replace with end-of-line comment change.to.end.of.line.comment.intention.family.name=Replace with End Of Line Comment -assert.to.if.intention.name=Replace assert with if statement +assert.to.if.intention.name=Replace 'assert' with 'if' statement assert.to.if.intention.family.name=Replace Assert with If Statement -replace.equality.with.safe.equals.intention.name=Replace == with safe .equals() +replace.equality.with.safe.equals.intention.name=Replace '==' with safe '.equals()' replace.equality.with.safe.equals.intention.family.name=Replace Equality with Safe Equals -replace.for.each.loop.with.indexed.for.loop.intention.name=Replace for-each loop with indexed for loop -replace.for.each.loop.with.iterator.for.loop.intention.name=Replace for-each loop with iterator for loop +replace.for.each.loop.with.indexed.for.loop.intention.name=Replace 'for each' loop with indexed 'for' loop +replace.for.each.loop.with.iterator.for.loop.intention.name=Replace 'for each' loop with iterator 'for' loop replace.for.each.loop.with.indexed.for.loop.intention.family.name=Replace For-each Loop with Indexed For Loop replace.for.each.loop.with.iterator.for.loop.intention.family.name=Replace For-each Loop with Iterator For Loop -replace.for.loop.with.while.loop.intention.name=Replace for loop with while loop +replace.for.loop.with.while.loop.intention.name=Replace 'for' loop with 'while' loop replace.for.loop.with.while.loop.intention.family.name=Replace For Loop with While Loop -replace.while.loop.with.do.while.loop.intention.name=Replace while loop with do-while loop +replace.while.loop.with.do.while.loop.intention.name=Replace 'while' loop with 'do while' loop replace.while.loop.with.do.while.loop.intention.family.name=Replace While Loop with Do While Loop -replace.do.while.loop.with.while.loop.intention.name=Replace do-while loop with while loop +replace.do.while.loop.with.while.loop.intention.name=Replace 'do while' loop with 'while' loop replace.do.while.loop.with.while.loop.intention.family.name=Replace Do While Loop with While Loop -expand.boolean.intention.name=Expand boolean use to if-else +expand.boolean.intention.name=Expand boolean use to 'if else' expand.boolean.intention.family.name=Expand Boolean -split.else.if.intention.name=Split else-if +split.else.if.intention.name=Split 'else if' split.else.if.intention.family.name=Split Else If join.concatenated.string.literals.intention.name=Join concatenated String literals join.concatenated.string.literals.intention.family.name=Join Concatenated String Literals -replace.switch.with.if.intention.name=Replace switch with if +replace.switch.with.if.intention.name=Replace 'switch' with 'if' replace.switch.with.if.intention.family.name=Replace Switch with If simplify.variable.intention.name=Replace with Java-style array declaration simplify.variable.intention.family.name=Replace with Java Style Array Declaration @@ -39,7 +39,7 @@ constant.expression.intention.name=Compute constant value constant.expression.intention.family.name=Compute Constant Value create.assert.intention.name=Create JUnit Assertion create.assert.intention.family.name=Create JUnit Assertion -simplify.if.else.intention.name=Simplify if-else +simplify.if.else.intention.name=Simplify 'if else' simplify.if.else.intention.family.name=Simplify If Else merge.if.or.intention.name=Merge sequential 'if's merge.if.or.intention.family.name=Merge Equivalent Ifs to ORed Condition @@ -65,7 +65,7 @@ string.to.char.intention.name=Replace string literal with character string.to.char.intention.family.name=Replace String with Char convert.integer.to.decimal.intention.name=Convert to decimal convert.integer.to.decimal.intention.family.name=Convert to Decimal -create.enum.switch.branches.intention.name=Create 'switch' branches +create.enum.switch.branches.intention.name=Create missing 'switch' branches create.enum.switch.branches.intention.family.name=Create Enum Switch Branches change.to.c.style.comment.intention.name=Replace with block comment change.to.c.style.comment.intention.family.name=Replace with Block Comment @@ -75,17 +75,17 @@ split.declaration.and.initialization.intention.name=Split into declaration and i split.declaration.and.initialization.intention.family.name=Split Declaration and Initialization char.to.string.intention.name=Replace character literal with string char.to.string.intention.family.name=Replace Char with String -replace.if.with.conditional.intention.name=Replace if-else with ?: +replace.if.with.conditional.intention.name=Replace 'if else' with '?:' replace.if.with.conditional.intention.family.name=Replace If Else with Conditional -replace.equality.with.equals.intention.name=Replace == with .equals() +replace.equality.with.equals.intention.name=Replace '==' with '.equals()' replace.equality.with.equals.intention.family.name=Replace Equality with Equals make.append.chain.into.append.sequence.intention.name=Make .append() chain into .append() sequence make.append.chain.into.append.sequence.intention.family.name=Make Append Chain Into Append Sequence detail.exceptions.intention.name=Detail exceptions detail.exceptions.intention.family.name=Detail Exceptions -flip.conditional.intention.name=Flip ?: +flip.conditional.intention.name=Flip '?:' flip.conditional.intention.family.name=Flip Conditional -replace.equals.with.equality.intention.name=Replace .equals() with == +replace.equals.with.equality.intention.name=Replace '.equals()' with '==' replace.equals.with.equality.intention.family.name=Replace Equals with Equality replace.shift.with.multiply.intention.family.name=Replace Shift with Multiply demorgans.intention.family.name=DeMorgan Law @@ -119,9 +119,9 @@ convert.catch.to.throws.intention.name=Replace catch section with throws declara convert.catch.to.throws.intention.family.name=Replace Catch Section with Throws Declaration wrap.vararg.arguments.with.explicit.array.intention.name=Wrap vararg arguments with explicit array creation wrap.vararg.arguments.with.explicit.array.intention.family.name=Wrap Vararg Arguments with Explicit Array Creation -extract.while.loop.condition.to.if.statement.intention.name=Extract condition to internal if statement +extract.while.loop.condition.to.if.statement.intention.name=Extract condition to internal 'if' statement extract.while.loop.condition.to.if.statement.intention.family.name=Extract While Loop Condition to Internal If Statement -replace.concatenation.with.format.string.intention.name=Replace + with String.format() call +replace.concatenation.with.format.string.intention.name=Replace '+ 'with 'String.format()' call replace.concatenation.with.format.string.intention.family.name=Replace String Concatenation with String.format() Call copy.concatenated.string.to.clipboard.intention.name=Copy String concatenation text to the clipboard copy.concatenated.string.to.clipboard.intention.family.name=Copy String Concatenation Text to the Clipboard @@ -137,7 +137,7 @@ replace.assignment.with.postfix.expression.intention.family.name=Replace Assignm convert.j.unit3.test.case.to.j.unit4.intention.family.name=Convert JUnit3 Test Case to JUnit4 convert.j.unit3.test.case.to.j.unit4.intention.name=Convert to JUnit4 Test Case if.to.assertion.intention.family.name=Replace If with Assert Statement -if.to.assertion.intention.name=Replace if with assert statement +if.to.assertion.intention.name=Replace 'if' with 'assert' statement expand.to.normal.annotation.intention.family.name=Expand Annotation to Normal Form expand.to.normal.annotation.name=Expand to ''{0}'' From bde180fb3f533f2368ad594ba595fe77ff8df853 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Sat, 19 Feb 2011 13:07:02 +0100 Subject: [PATCH 28/38] switch off excessive logging --- .../javaCompiler/BackendCompilerWrapper.java | 4 -- .../javaCompiler/CompilerParsingThread.java | 44 +------------------ 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java index 9a25ec6014f8..181485b6b505 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java @@ -377,10 +377,6 @@ public class BackendCompilerWrapper { exitValue = process.waitFor(); } catch (InterruptedException e) { - if (CompileDriver.ourDebugMode) { - System.out.println("Compiler interrupted; "); - e.printStackTrace(); - } process.destroy(); exitValue = process.exitValue(); } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java index dcb4d16d33d1..38b6b2f8e39d 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java @@ -48,8 +48,6 @@ public class CompilerParsingThread implements Runnable, OutputParser.Callback { private volatile boolean myProcessExited = false; private final CompileContext myContext; - //private final BlockingQueue myLines = new LinkedBlockingQueue(); - public CompilerParsingThread(Process process, OutputParser outputParser, final boolean readErrorStream, boolean trimLines, CompileContext context) { myProcess = process; myOutputParser = outputParser; @@ -62,24 +60,6 @@ public class CompilerParsingThread implements Runnable, OutputParser.Callback { volatile boolean processing; public void run() { - //if (CompileDriver.ourDebugMode) { - // ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - // @Override - // public void run() { - // while (true) { - // final String line = readLine(myCompilerOutStreamReader); - // if (CompileDriver.ourDebugMode) { - // System.out.println("RAW_LIne read: #" + line + "#"); - // } - // if (line == null) { - // myLines.offer(TERMINATION_STRING); - // break; - // } - // myLines.offer(line); - // } - // } - // }); - //} processing = true; try { while (true) { @@ -131,7 +111,7 @@ public class CompilerParsingThread implements Runnable, OutputParser.Callback { myLastReadLine = pushBack; return pushBack; } - final String line = getNextUnprocessedLine(); + final String line = readLine(myCompilerOutStreamReader); if (LOG.isDebugEnabled()) { LOG.debug("LIne read: #" + line + "#"); } @@ -147,28 +127,6 @@ public class CompilerParsingThread implements Runnable, OutputParser.Callback { return myLastReadLine; } - private String getNextUnprocessedLine() { - //if (CompileDriver.ourDebugMode) { - // try { - // if (TERMINATION_STRING.equals(myLines.peek())) { - // return TERMINATION_STRING; - // } - // final String line = myLines.take(); - // if (TERMINATION_STRING.equals(line)) { - // myLines.offer(TERMINATION_STRING); // pushback - // } - // return line; - // } - // catch (InterruptedException e) { - // e.printStackTrace(); - // return TERMINATION_STRING; - // } - //} - //else { - return readLine(myCompilerOutStreamReader); - //} - } - @Override public void pushBack(String line) { myLastReadLine = null; From ee870144fef5c59bf314787b9965c58c50606620 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 18 Feb 2011 20:06:34 +0100 Subject: [PATCH 29/38] To JB dictionary --- plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic b/plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic index 79d88ab70168..b86b0bef6f9a 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic +++ b/plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic @@ -40,6 +40,7 @@ classpath clazz clob closable +closeable cmdline commandline config From 85f277a149528586f7259dcb39eeec76c4aba661 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sat, 19 Feb 2011 13:33:51 +0100 Subject: [PATCH 30/38] Mock JDK 7 updated (AutoCloseable, Throwable) --- java/mockJDK-1.7/jre/lib/rt.jar | Bin 586886 -> 586243 bytes java/mockJDK-1.7/src.zip | Bin 803939 -> 805928 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/java/mockJDK-1.7/jre/lib/rt.jar b/java/mockJDK-1.7/jre/lib/rt.jar index 6009cb2194d518d7d8783620e2426f1aab666b34..4bc199ad9ad45e3a86fecdffe4d92d9531036b73 100644 GIT binary patch delta 21950 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 ZJ*L8?#ZaBZ^kkYYguimO8%mWJ{|_?_kJ|tM delta 24426 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

LcI$=9^ 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 Date: Sat, 19 Feb 2011 14:48:11 +0100 Subject: [PATCH 31/38] Multi-catch support: more highlighting --- .../daemon/impl/analysis/HighlightUtil.java | 45 +++++++++++++------ .../impl/analysis/HighlightVisitorImpl.java | 2 +- .../advHighlighting7/MultiCatch.java | 3 +- .../advHighlighting7/MultiCatchWarn.java | 12 +++++ .../daemon/LightAdvHighlightingJdk7Test.java | 4 ++ .../src/messages/JavaErrorMessages.properties | 1 + 6 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MultiCatchWarn.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 3d26c4e340fc..ec903511c0e1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.daemon.impl.analysis; +import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInsight.daemon.JavaErrorMessages; @@ -852,7 +853,7 @@ public class HighlightUtil { @Nullable - static HighlightInfo checkExceptionThrownInTry(final PsiParameter parameter) { + static Collection checkExceptionThrownInTry(final PsiParameter parameter) { final PsiElement declarationScope = parameter.getDeclarationScope(); if (!(declarationScope instanceof PsiCatchSection)) return null; @@ -882,9 +883,9 @@ public class HighlightUtil { } @Nullable - private static HighlightInfo checkSimpleCatchParameter(final PsiParameter parameter, - final Collection thrownTypes, - final PsiClassType caughtType) { + private static Collection checkSimpleCatchParameter(final PsiParameter parameter, + final Collection thrownTypes, + final PsiClassType caughtType) { if (ExceptionUtil.isUncheckedExceptionOrSuperclass(caughtType)) return null; for (PsiClassType exceptionType : thrownTypes) { @@ -894,12 +895,17 @@ public class HighlightUtil { final String description = JavaErrorMessages.message("exception.never.thrown.try", formatType(caughtType)); final HighlightInfo errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parameter, description); QuickFixAction.registerQuickFixAction(errorResult, new DeleteCatchFix(parameter)); - return errorResult; + return Lists.newArrayList(errorResult); } @Nullable - private static HighlightInfo checkMultiCatchParameter(final PsiParameter parameter, final Collection thrownTypes) { - for (PsiTypeElement typeElement : PsiTreeUtil.getChildrenOfTypeAsList(parameter.getTypeElement(), PsiTypeElement.class)) { + private static Collection checkMultiCatchParameter(final PsiParameter parameter, + final Collection thrownTypes) { + final List typeElements = PsiTreeUtil.getChildrenOfTypeAsList(parameter.getTypeElement(), PsiTypeElement.class); + final Collection highlights = Lists.newArrayListWithCapacity(typeElements.size()); + + for (int i = 0, size = typeElements.size(); i < size; i++) { + final PsiTypeElement typeElement = typeElements.get(i); final PsiType catchType = typeElement.getType(); if (catchType instanceof PsiClassType && ExceptionUtil.isUncheckedExceptionOrSuperclass((PsiClassType)catchType)) continue; @@ -910,15 +916,28 @@ public class HighlightUtil { break; } } - if (used) continue; + if (!used) { + final String description = JavaErrorMessages.message("exception.never.thrown.try", formatType(catchType)); + final HighlightInfo highlight = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeElement, description); + QuickFixAction.registerQuickFixAction(highlight, new DeleteMultiCatchFix(typeElement)); + highlights.add(highlight); + continue; + } - final String description = JavaErrorMessages.message("exception.never.thrown.try", formatType(catchType)); - final HighlightInfo errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeElement, description); - QuickFixAction.registerQuickFixAction(errorResult, new DeleteMultiCatchFix(typeElement)); - return errorResult; + for (int j = size - 1; j > i; j--) { + final PsiTypeElement nextElement = typeElements.get(j); + final PsiType nextType = nextElement.getType(); + if (nextType.isAssignableFrom(catchType)) { + final String description = JavaErrorMessages.message("exception.double.caught.in.multi", formatType(catchType), formatType(nextType)); + final HighlightInfo highlight = HighlightInfo.createHighlightInfo(HighlightInfoType.WARNING, typeElement, description); + QuickFixAction.registerQuickFixAction(highlight, new DeleteMultiCatchFix(typeElement)); + highlights.add(highlight); + break; + } + } } - return null; + return highlights; } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index e4e7d8ebf7c3..9d963c68c0d2 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -899,7 +899,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh if (!myHolder.hasErrorResults()) { PsiParameter[] parameters = statement.getCatchBlockParameters(); for (PsiParameter parameter : parameters) { - myHolder.add(HighlightUtil.checkExceptionThrownInTry(parameter)); + myHolder.addAll(HighlightUtil.checkExceptionThrownInTry(parameter)); myHolder.add(HighlightUtil.checkCatchParameterIsThrowable(parameter)); myHolder.add(GenericsHighlightUtil.checkCatchParameterIsClass(parameter)); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MultiCatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MultiCatch.java index 705ecd8a6550..e33d031f7868 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MultiCatch.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MultiCatch.java @@ -4,6 +4,7 @@ abstract class C { private static class E1 extends E { } private static class E2 extends E { } private static class E3 extends E { } + private static class E4 extends E { } private static class RE extends RuntimeException { } private interface I { } private static class IE1 extends E implements I { } @@ -23,7 +24,7 @@ abstract class C { try { g(); } catch (IE1 | IE2 e) { new F>(e); } try { f(); } catch (E1 | E2 | E3 e) { } - try { f(); } catch (E3 | E e) { } + try { f(); } catch (E3 | E4 | E e) { } try { f(); } catch (E | E1 e) { } try { f(); } catch (E | E3 e) { } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MultiCatchWarn.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MultiCatchWarn.java new file mode 100644 index 000000000000..d7e9796887c0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MultiCatchWarn.java @@ -0,0 +1,12 @@ +abstract class C { + private static class E extends Exception { } + private static class E1 extends E { } + private static class E2 extends E { } + + abstract void f() throws E1, E2; + + void m() { + try { f(); } catch (E1 | E2 ignore) { } + try { f(); } catch (E1 | E ignore) { } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java index 8ce6d97e1c8d..6b64c70d82de 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java @@ -154,6 +154,10 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase { doTest(false, false); } + public void testMultiCatchWarn() throws Exception { + doTest(true, false); + } + public void testTryWithResources() throws Exception { doTest(false, false); } diff --git a/resources-en/src/messages/JavaErrorMessages.properties b/resources-en/src/messages/JavaErrorMessages.properties index cefabbeed699..7ac5ab1b4dd7 100644 --- a/resources-en/src/messages/JavaErrorMessages.properties +++ b/resources-en/src/messages/JavaErrorMessages.properties @@ -224,6 +224,7 @@ nonterminated.comment=Unclosed comment assignment.to.itself=Variable is assigned to itself assignment.to.declared.variable=Variable ''{0}'' is initialized with self assignment exception.already.caught=Exception ''{0}'' has already been caught +exception.double.caught.in.multi=Exception ''{0}'' is also caught by ''{1}'' statement.must.be.prepended.with.case.label=Statement must be prepended with case label void.type.is.not.allowed='void' type is not allowed here single.import.class.conflict=''{0}'' is already defined in a single-type import From 5fb59a55fd01e9802c7a599b10ec867465021e77 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Sat, 19 Feb 2011 17:10:17 +0100 Subject: [PATCH 32/38] cleanup --- .../OverlyComplexBooleanExpressionInspection.java | 14 ++++++++++---- .../SwitchStatementDensityInspection.java | 10 +++++++--- ...verlyComplexArithmeticExpressionInspection.java | 12 +++++++++--- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/OverlyComplexBooleanExpressionInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/OverlyComplexBooleanExpressionInspection.java index c9725f5e5e6c..136350d7a3a0 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/OverlyComplexBooleanExpressionInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/OverlyComplexBooleanExpressionInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,18 +61,21 @@ public class OverlyComplexBooleanExpressionInspection /** @noinspection PublicField*/ public boolean m_ignorePureConjunctionsDisjunctions = true; + @Override @NotNull public String getDisplayName() { return InspectionGadgetsBundle.message( "overly.complex.boolean.expression.display.name"); } + @Override @NotNull protected String buildErrorString(Object... infos) { return InspectionGadgetsBundle.message( "overly.complex.boolean.expression.problem.descriptor"); } + @Override public JComponent createOptionsPanel() { final ButtonModel pureModel = m_ignoreConjunctionsDisjunctionsCheckBox.getModel(); @@ -85,7 +88,7 @@ public class OverlyComplexBooleanExpressionInspection }); final NumberFormat formatter = NumberFormat.getIntegerInstance(); formatter.setParseIntegerOnly(true); - m_termLimitTextField.setValue(m_limit); + m_termLimitTextField.setValue(Integer.valueOf(m_limit)); m_termLimitTextField.setColumns(4); FormattedTextFieldMacFix.apply(m_termLimitTextField); final Document document = m_termLimitTextField.getDocument(); @@ -115,19 +118,22 @@ public class OverlyComplexBooleanExpressionInspection return m_contentPanel; } + @Override protected boolean buildQuickFixesOnlyForOnTheFlyErrors(){ return true; } + @Override protected InspectionGadgetsFix buildFix(Object... infos){ return new ExtractMethodFix(); } + @Override public BaseInspectionVisitor buildVisitor() { - return new SwitchStatementWithTooManyBranchesVisitor(); + return new OverlyComplexBooleanExpressionVisitor(); } - private class SwitchStatementWithTooManyBranchesVisitor + private class OverlyComplexBooleanExpressionVisitor extends BaseInspectionVisitor { @Override public void visitBinaryExpression( diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/SwitchStatementDensityInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/SwitchStatementDensityInspection.java index 108a65ec5e90..6475f74b6abb 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/SwitchStatementDensityInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/SwitchStatementDensityInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,12 +37,14 @@ public class SwitchStatementDensityInspection extends BaseInspection { */ public int m_limit = DEFAULT_DENSITY_LIMIT; + @Override @NotNull public String getDisplayName() { return InspectionGadgetsBundle.message( "switch.statement.density.display.name"); } + @Override public JComponent createOptionsPanel() { return new SingleIntegerFieldOptionsPanel( InspectionGadgetsBundle.message( @@ -50,6 +52,7 @@ public class SwitchStatementDensityInspection extends BaseInspection { "m_limit"); } + @Override @NotNull protected String buildErrorString(Object... infos) { final Integer intDensity = (Integer)infos[0]; @@ -57,11 +60,12 @@ public class SwitchStatementDensityInspection extends BaseInspection { "switch.statement.density.problem.descriptor", intDensity); } + @Override public BaseInspectionVisitor buildVisitor() { - return new SwitchStatementWithTooFewBranchesVisitor(); + return new SwitchStatementDensityVisitor(); } - private class SwitchStatementWithTooFewBranchesVisitor + private class SwitchStatementDensityVisitor extends BaseInspectionVisitor { @Override public void visitSwitchStatement( diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/OverlyComplexArithmeticExpressionInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/OverlyComplexArithmeticExpressionInspection.java index 1407a1290823..434257b08ba2 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/OverlyComplexArithmeticExpressionInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/OverlyComplexArithmeticExpressionInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,18 +48,21 @@ public class OverlyComplexArithmeticExpressionInspection arithmeticTokens.add("%"); } + @Override @NotNull public String getDisplayName() { return InspectionGadgetsBundle.message( "overly.complex.arithmetic.expression.display.name"); } + @Override @NotNull protected String buildErrorString(Object... infos) { return InspectionGadgetsBundle.message( "overly.complex.arithmetic.expression.problem.descriptor"); } + @Override public JComponent createOptionsPanel() { return new SingleIntegerFieldOptionsPanel( InspectionGadgetsBundle.message( @@ -67,19 +70,22 @@ public class OverlyComplexArithmeticExpressionInspection this, "m_limit"); } + @Override protected InspectionGadgetsFix buildFix(Object... infos) { return new ExtractMethodFix(); } + @Override protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { return true; } + @Override public BaseInspectionVisitor buildVisitor() { - return new SwitchStatementWithTooManyBranchesVisitor(); + return new OverlyComplexArithmeticExpressionVisitor(); } - private class SwitchStatementWithTooManyBranchesVisitor + private class OverlyComplexArithmeticExpressionVisitor extends BaseInspectionVisitor { @Override public void visitBinaryExpression( From 985f2bbfd6bdc799a88ea1698bb72be377657b46 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Sat, 19 Feb 2011 17:54:29 +0100 Subject: [PATCH 33/38] add configuration to specify minimum number of case branches --- .../siyeh/InspectionGadgetsBundle.properties | 1 + .../controlflow/IfCanBeSwitchInspection.java | 113 ++++++++---------- .../com/siyeh/ig/psiutils/SwitchUtils.java | 11 +- .../inspectionDescriptions/IfCanBeSwitch.html | 2 +- 4 files changed, 60 insertions(+), 67 deletions(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index 0e7f1fa35ba4..8cad05468417 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1850,3 +1850,4 @@ try.with.identical.catches.problem.descriptor=Identical 'catch' branches in 'try if.can.be.switch.display.name='if' replaceable with 'switch' if.can.be.switch.problem.descriptor=#ref statement replaceable with 'switch' statement if.can.be.switch.quickfix=Replace with 'switch' +if.can.be.switch.minimum.branch.option=Minimum number of branches diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java index 86e9bc3e6e59..0a03f433fd0f 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java @@ -16,6 +16,7 @@ package com.siyeh.ig.controlflow; import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ui.SingleIntegerFieldOptionsPanel; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; @@ -33,11 +34,15 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.util.ArrayList; import java.util.List; public class IfCanBeSwitchInspection extends BaseInspection { + @SuppressWarnings({"PublicField"}) + public int minimumBranches = 3; + @Nls @NotNull @Override @@ -57,6 +62,14 @@ public class IfCanBeSwitchInspection extends BaseInspection { return new IfCanBeSwitchFix((PsiExpression) infos[0]); } + @Override + public JComponent createOptionsPanel() { + return new SingleIntegerFieldOptionsPanel( + InspectionGadgetsBundle.message( + "if.can.be.switch.minimum.branch.option"), + this, "minimumBranches"); + } + private static class IfCanBeSwitchFix extends InspectionGadgetsFix { private final PsiExpression switchExpression; @@ -168,25 +181,8 @@ public class IfCanBeSwitchInspection extends BaseInspection { hasConflicts = true; } } - - final PsiStatement branchStatement = branch.getStatement(); - if (branch.isElse()) { - final List comments = branch.getComments(); - final List statementComments = - branch.getStatementComments(); - dumpDefaultBranch(switchStatementText, comments, - branchStatement, statementComments, - hasConflicts, breaksNeedRelabeled, labelString); - } else { - final List conditions = branch.getConditions(); - final List comments = branch.getComments(); - final List statementComments = - branch.getStatementComments(); - dumpBranch(switchStatementText, - comments, conditions, statementComments, - branchStatement, hasConflicts, breaksNeedRelabeled, - labelString); - } + dumpBranch(branch, hasConflicts, breaksNeedRelabeled, + labelString, switchStatementText); } switchStatementText.append('}'); final JavaPsiFacade psiFacade = @@ -346,57 +342,41 @@ public class IfCanBeSwitchInspection extends BaseInspection { return values; } - private static void dumpBranch(StringBuilder switchStatementText, - List comments, - List labels, - List statementComments, - PsiStatement body, - boolean wrap, boolean renameBreaks, - String breakLabelName) { - dumpComments(switchStatementText, comments); - dumpLabels(switchStatementText, labels); - dumpComments(switchStatementText, statementComments); - dumpBody(switchStatementText, body, wrap, renameBreaks, - breakLabelName); - } - - private static void dumpComments(StringBuilder switchStatementText, - List comments) { - if (!comments.isEmpty()) { - switchStatementText.append('\n'); - for (String comment : comments) { - switchStatementText.append(comment); - switchStatementText.append('\n'); + private static void dumpBranch( + IfStatementBranch branch, boolean wrap, + boolean renameBreaks, String breakLabelName, + @NonNls StringBuilder switchStatementText) { + dumpComments(branch.getComments(), switchStatementText); + if (branch.isElse()) { + switchStatementText.append("default: "); + } else { + for (String label : branch.getConditions()) { + switchStatementText.append("case "); + switchStatementText.append(label); + switchStatementText.append(": "); } } + dumpComments(branch.getStatementComments(), switchStatementText); + dumpBody(branch.getStatement(), wrap, renameBreaks, breakLabelName, + switchStatementText); } - private static void dumpDefaultBranch( - @NonNls StringBuilder switchStatementString, - List comments, PsiStatement body, - List statementComments, boolean wrap, - boolean renameBreaks, String breakLabelName) { - dumpComments(switchStatementString, comments); - switchStatementString.append("default: "); - dumpComments(switchStatementString, statementComments); - dumpBody(switchStatementString, body, wrap, renameBreaks, - breakLabelName); - } - - private static void dumpLabels( - @NonNls StringBuilder switchStatementText, - List labels) { - for (String label : labels) { - switchStatementText.append("case "); - switchStatementText.append(label); - switchStatementText.append(": "); + private static void dumpComments(List comments, + StringBuilder switchStatementText) { + if (comments.isEmpty()) { + return; + } + switchStatementText.append('\n'); + for (String comment : comments) { + switchStatementText.append(comment); + switchStatementText.append('\n'); } } private static void dumpBody( - @NonNls StringBuilder switchStatementText, - PsiStatement bodyStatement, boolean wrap, - boolean renameBreaks, String breakLabelName) { + PsiStatement bodyStatement, boolean wrap, boolean renameBreaks, + String breakLabelName, + @NonNls StringBuilder switchStatementText) { if (wrap) { switchStatementText.append('{'); } @@ -466,18 +446,21 @@ public class IfCanBeSwitchInspection extends BaseInspection { return new IfCanBeSwitchVisitor(); } - private static class IfCanBeSwitchVisitor extends BaseInspectionVisitor { + private class IfCanBeSwitchVisitor extends BaseInspectionVisitor { @Override public void visitIfStatement(PsiIfStatement statement) { super.visitIfStatement(statement); + final PsiElement parent = statement.getParent(); + if (parent instanceof PsiIfStatement) { + return; + } final PsiExpression switchExpression = - SwitchUtils.getSwitchExpression(statement); + SwitchUtils.getSwitchExpression(statement, minimumBranches); if (switchExpression == null) { return; } registerStatementError(statement, switchExpression); } } - } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/SwitchUtils.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/SwitchUtils.java index 34927faa3472..e319ad78ca59 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/SwitchUtils.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/SwitchUtils.java @@ -44,7 +44,8 @@ public class SwitchUtils{ } @Nullable - public static PsiExpression getSwitchExpression(PsiIfStatement statement){ + public static PsiExpression getSwitchExpression(PsiIfStatement statement, + int minimumBranches){ final PsiExpression condition = statement.getCondition(); final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(statement); @@ -56,7 +57,9 @@ public class SwitchUtils{ if (SideEffectChecker.mayHaveSideEffects(possibleSwitchExpression)) { return null; } + int branchCount = 0; while(true){ + branchCount++; final PsiExpression caseCondition = statement.getCondition(); if (!canBeMadeIntoCase(caseCondition, possibleSwitchExpression, languageLevel)) { @@ -64,6 +67,12 @@ public class SwitchUtils{ } final PsiStatement elseBranch = statement.getElseBranch(); if(!(elseBranch instanceof PsiIfStatement)){ + if (elseBranch != null) { + branchCount++; + } + if (branchCount < minimumBranches) { + return null; + } return possibleSwitchExpression; } statement = (PsiIfStatement) elseBranch; diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/IfCanBeSwitch.html b/plugins/InspectionGadgets/src/inspectionDescriptions/IfCanBeSwitch.html index 5130a10fd67f..6fb425c8f6bc 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/IfCanBeSwitch.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/IfCanBeSwitch.html @@ -4,7 +4,7 @@ This inspection reports any if statements with which can be replaced by a switch statement. This inspection will automatically suggest string switches when the project language level is jdk 1.7 or higher. Use the text field below to indicate the minimum -number of case branches the resulting switch statement should have. +number of case branches the resulting switch statement should have. New in 11?, Powered by InspectionGadgets \ No newline at end of file From 686129f0fba1a859d869a8cd9b7f0ccf82348da8 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sat, 19 Feb 2011 20:22:04 +0100 Subject: [PATCH 34/38] Test data fixed --- .../completion/className/importAfterNew/after1.java | 4 ++-- .../completion/className/importAfterNew/before1.java | 2 +- .../className/importAfterNew/source/pack/AAClass.java | 4 ++++ .../className/importAfterNew/source/pack/AClass.java | 4 ---- 4 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/className/importAfterNew/source/pack/AAClass.java delete mode 100644 java/java-tests/testData/codeInsight/completion/className/importAfterNew/source/pack/AClass.java diff --git a/java/java-tests/testData/codeInsight/completion/className/importAfterNew/after1.java b/java/java-tests/testData/codeInsight/completion/className/importAfterNew/after1.java index e97c0f13e918..82978259fafe 100644 --- a/java/java-tests/testData/codeInsight/completion/className/importAfterNew/after1.java +++ b/java/java-tests/testData/codeInsight/completion/className/importAfterNew/after1.java @@ -1,7 +1,7 @@ -import pack.AClass; +import pack.AAClass; public class Test1 { public void foo() { - new AClass() + new AAClass() } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/className/importAfterNew/before1.java b/java/java-tests/testData/codeInsight/completion/className/importAfterNew/before1.java index 7c418f81e585..1643269e9d4c 100644 --- a/java/java-tests/testData/codeInsight/completion/className/importAfterNew/before1.java +++ b/java/java-tests/testData/codeInsight/completion/className/importAfterNew/before1.java @@ -1,5 +1,5 @@ public class Test1 { public void foo() { - new ACl + new AACl } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/className/importAfterNew/source/pack/AAClass.java b/java/java-tests/testData/codeInsight/completion/className/importAfterNew/source/pack/AAClass.java new file mode 100644 index 000000000000..052f73bfea69 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/className/importAfterNew/source/pack/AAClass.java @@ -0,0 +1,4 @@ +package pack; + +public class AAClass { +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/className/importAfterNew/source/pack/AClass.java b/java/java-tests/testData/codeInsight/completion/className/importAfterNew/source/pack/AClass.java deleted file mode 100644 index 0e67a315a1ae..000000000000 --- a/java/java-tests/testData/codeInsight/completion/className/importAfterNew/source/pack/AClass.java +++ /dev/null @@ -1,4 +0,0 @@ -package pack; - -public class AClass{ -} \ No newline at end of file From 6cca1258deb0d4aa15abe070b29e6b53eeb7b48b Mon Sep 17 00:00:00 2001 From: "kirill.safonov" Date: Sun, 20 Feb 2011 00:41:50 +0300 Subject: [PATCH 35/38] Move AS/Flex class dialog: 'search for text occurences', 'move to different source root' checkboxes --- .../MoveClassesOrPackagesUtil.java | 30 ++------------- .../ide/util/PlatformPackageUtil.java | 37 ++++++++++-------- .../MoveFilesOrDirectoriesProcessor.java | 4 +- .../refactoring/util/TextOccurrencesUtil.java | 38 +++++++++++++++++-- 4 files changed, 61 insertions(+), 48 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesUtil.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesUtil.java index 90b3bc1d180a..90494fe0d2f0 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesUtil.java @@ -30,13 +30,11 @@ import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.refactoring.MoveDestination; import com.intellij.refactoring.PackageWrapper; import com.intellij.refactoring.util.MoveRenameUsageInfo; -import com.intellij.refactoring.util.NonCodeUsageInfo; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.refactoring.util.TextOccurrencesUtil; import com.intellij.usageView.UsageInfo; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; -import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.*; @@ -97,31 +95,9 @@ public class MoveClassesOrPackagesUtil { final PsiElement element, final String newQName, ArrayList results) { - if (searchInStringsAndComments || searchInNonJavaFiles) { - final String stringToSearch = getStringToSearch(element); - if (stringToSearch == null) return; - TextOccurrencesUtil.UsageInfoFactory factory = createUsageInfoFactory(element, newQName); - - if (searchInStringsAndComments) { - TextOccurrencesUtil.addUsagesInStringsAndComments(element, stringToSearch, results, factory); - } - - if (searchInNonJavaFiles) { - GlobalSearchScope projectScope = GlobalSearchScope.projectScope(element.getProject()); - TextOccurrencesUtil.addTextOccurences(element, stringToSearch, projectScope, results, factory); - } - } - } - - private static TextOccurrencesUtil.UsageInfoFactory createUsageInfoFactory(final PsiElement element, - final String newQName) { - return new TextOccurrencesUtil.UsageInfoFactory() { - public UsageInfo createUsageInfo(@NotNull PsiElement usage, int startOffset, int endOffset) { - int start = usage.getTextRange().getStartOffset(); - return NonCodeUsageInfo.create(usage.getContainingFile(), start + startOffset, start + endOffset, element, - newQName); - } - }; + final String stringToSearch = getStringToSearch(element); + if (stringToSearch == null) return; + TextOccurrencesUtil.findNonCodeUsages(element, stringToSearch, searchInStringsAndComments, searchInNonJavaFiles, newQName, results); } private static String getStringToSearch(PsiElement element) { diff --git a/platform/lang-impl/src/com/intellij/ide/util/PlatformPackageUtil.java b/platform/lang-impl/src/com/intellij/ide/util/PlatformPackageUtil.java index 731c2f661715..62c13ef62938 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/PlatformPackageUtil.java +++ b/platform/lang-impl/src/com/intellij/ide/util/PlatformPackageUtil.java @@ -90,9 +90,10 @@ public class PlatformPackageUtil { GlobalSearchScope scope, String packageName, PsiDirectory baseDir, - boolean askUserToCreate) throws IncorrectOperationException { + boolean askUserToCreate, + ThreeState chooseFlag) throws IncorrectOperationException { PsiDirectory psiDirectory = null; - if (!"".equals(packageName)) { + if (chooseFlag == ThreeState.UNSURE && !"".equals(packageName)) { String rootPackage = findLongestExistingPackage(project, packageName, scope); if (rootPackage != null) { int beginIndex = rootPackage.length() + 1; @@ -108,19 +109,25 @@ public class PlatformPackageUtil { } if (psiDirectory == null) { - if (module != null && !checkSourceRootsConfigured(module)) return null; - final GlobalSearchScope scope_ = scope; - List dirs = - ContainerUtil - .mapNotNull(ProjectRootManager.getInstance(project).getContentSourceRoots(), new Function() { - @Override - public PsiDirectory fun(VirtualFile virtualFile) { - return scope_.contains(virtualFile) ? PsiManager.getInstance(project).findDirectory(virtualFile) : null; - } - }); - psiDirectory = DirectoryChooserUtil.selectDirectory(project, dirs.toArray(new PsiDirectory[dirs.size()]), baseDir, - File.separatorChar + packageName.replace('.', File.separatorChar)); - if (psiDirectory == null) return null; + if (chooseFlag == ThreeState.NO && baseDir != null) { + VirtualFile sourceRoot = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(baseDir.getVirtualFile()); + psiDirectory = PsiManager.getInstance(project).findDirectory(sourceRoot); + } + else { + if (module != null && !checkSourceRootsConfigured(module)) return null; + final GlobalSearchScope scope_ = scope; + List dirs = + ContainerUtil + .mapNotNull(ProjectRootManager.getInstance(project).getContentSourceRoots(), new Function() { + @Override + public PsiDirectory fun(VirtualFile virtualFile) { + return scope_.contains(virtualFile) ? PsiManager.getInstance(project).findDirectory(virtualFile) : null; + } + }); + psiDirectory = DirectoryChooserUtil.selectDirectory(project, dirs.toArray(new PsiDirectory[dirs.size()]), baseDir, + File.separatorChar + packageName.replace('.', File.separatorChar)); + if (psiDirectory == null) return null; + } } String restOfName = packageName; diff --git a/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java index eb900485fc7d..605919457db2 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java @@ -47,8 +47,8 @@ public class MoveFilesOrDirectoriesProcessor extends BaseRefactoringProcessor { protected final PsiElement[] myElementsToMove; private final boolean mySearchForReferences; - private final boolean mySearchInComments; - private final boolean mySearchInNonJavaFiles; + protected final boolean mySearchInComments; + protected final boolean mySearchInNonJavaFiles; private final PsiDirectory myNewParent; private final MoveCallback myMoveCallback; private NonCodeUsageInfo[] myNonCodeUsages; diff --git a/platform/lang-impl/src/com/intellij/refactoring/util/TextOccurrencesUtil.java b/platform/lang-impl/src/com/intellij/refactoring/util/TextOccurrencesUtil.java index 4afb2655df47..3bf81139b8e4 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/util/TextOccurrencesUtil.java +++ b/platform/lang-impl/src/com/intellij/refactoring/util/TextOccurrencesUtil.java @@ -25,14 +25,17 @@ import com.intellij.lang.ASTNode; import com.intellij.lang.LanguageParserDefinitions; import com.intellij.lang.ParserDefinition; import com.intellij.openapi.util.TextRange; -import com.intellij.psi.*; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiReference; import com.intellij.psi.search.*; import com.intellij.usageView.UsageInfo; import com.intellij.util.PairProcessor; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; -import java.util.List; +import java.util.Collection; public class TextOccurrencesUtil { private TextOccurrencesUtil() { @@ -41,7 +44,7 @@ public class TextOccurrencesUtil { public static void addTextOccurences(@NotNull PsiElement element, @NotNull String stringToSearch, @NotNull GlobalSearchScope searchScope, - @NotNull final List results, + @NotNull final Collection results, @NotNull final UsageInfoFactory factory) { processTextOccurences(element, stringToSearch, searchScope, new Processor() { public boolean process(UsageInfo t) { @@ -104,7 +107,7 @@ public class TextOccurrencesUtil { public static void addUsagesInStringsAndComments(@NotNull PsiElement element, @NotNull String stringToSearch, - @NotNull final List results, + @NotNull final Collection results, @NotNull final UsageInfoFactory factory) { processUsagesInStringsAndComments(element, stringToSearch, false, new PairProcessor() { public boolean process(PsiElement commentOrLiteral, TextRange textRange) { @@ -155,6 +158,33 @@ public class TextOccurrencesUtil { return FindUsagesUtil.isSearchForTextOccurrencesAvailable(element, false, handler); } + public static void findNonCodeUsages(PsiElement element, String stringToSearch, boolean searchInStringsAndComments, + boolean searchInNonJavaFiles, String newQName, Collection results) { + if (searchInStringsAndComments || searchInNonJavaFiles) { + UsageInfoFactory factory = createUsageInfoFactory(element, newQName); + + if (searchInStringsAndComments) { + addUsagesInStringsAndComments(element, stringToSearch, results, factory); + } + + if (searchInNonJavaFiles) { + GlobalSearchScope projectScope = GlobalSearchScope.projectScope(element.getProject()); + addTextOccurences(element, stringToSearch, projectScope, results, factory); + } + } + } + + private static UsageInfoFactory createUsageInfoFactory(final PsiElement element, + final String newQName) { + return new UsageInfoFactory() { + public UsageInfo createUsageInfo(@NotNull PsiElement usage, int startOffset, int endOffset) { + int start = usage.getTextRange().getStartOffset(); + return NonCodeUsageInfo.create(usage.getContainingFile(), start + startOffset, start + endOffset, element, + newQName); + } + }; + } + public interface UsageInfoFactory { UsageInfo createUsageInfo(@NotNull PsiElement usage, int startOffset, int endOffset); } From 970250fd72575c033f0372d07d1f680e535d95e1 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Sun, 20 Feb 2011 09:13:09 +0100 Subject: [PATCH 36/38] less code --- .../ReplaceIfWithSwitchIntention.java | 120 +++++++----------- 1 file changed, 46 insertions(+), 74 deletions(-) diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java index add5b6fd8878..c35f86c1ce6a 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/ReplaceIfWithSwitchIntention.java @@ -128,8 +128,7 @@ public class ReplaceIfWithSwitchIntention extends Intention { new StringBuilder(); switchStatementText.append("switch("); switchStatementText.append(switchExpression.getText()); - switchStatementText.append(')'); - switchStatementText.append('{'); + switchStatementText.append("){"); for (IfStatementBranch branch : branches) { boolean hasConflicts = false; for (IfStatementBranch testBranch : branches) { @@ -140,25 +139,8 @@ public class ReplaceIfWithSwitchIntention extends Intention { hasConflicts = true; } } - - final PsiStatement branchStatement = branch.getStatement(); - if (branch.isElse()) { - final List comments = branch.getComments(); - final List statementComments = - branch.getStatementComments(); - dumpDefaultBranch(switchStatementText, comments, - branchStatement, statementComments, - hasConflicts, breaksNeedRelabeled, labelString); - } else { - final List conditions = branch.getConditions(); - final List comments = branch.getComments(); - final List statementComments = - branch.getStatementComments(); - dumpBranch(switchStatementText, - comments, conditions, statementComments, - branchStatement, hasConflicts, breaksNeedRelabeled, - labelString); - } + dumpBranch(branch, hasConflicts, breaksNeedRelabeled, labelString, + switchStatementText); } switchStatementText.append('}'); final JavaPsiFacade psiFacade = @@ -170,8 +152,8 @@ public class ReplaceIfWithSwitchIntention extends Intention { out.append(labelString); out.append(':'); } - termReplace(out, breakTarget, statementToReplace, - switchStatementText); + termReplace(breakTarget, statementToReplace, switchStatementText, + out); final String newStatementText = out.toString(); final PsiStatement newStatement = factory.createStatementFromText(newStatementText, element); @@ -253,8 +235,8 @@ public class ReplaceIfWithSwitchIntention extends Intention { } private static void termReplace( - StringBuilder out, PsiElement target, - PsiElement replace, StringBuilder stringToReplaceWith) { + PsiElement target, PsiElement replace, + StringBuilder stringToReplaceWith, StringBuilder out) { if (target.equals(replace)) { out.append(stringToReplaceWith); } else if (target.getChildren().length == 0) { @@ -262,7 +244,7 @@ public class ReplaceIfWithSwitchIntention extends Intention { } else { final PsiElement[] children = target.getChildren(); for (final PsiElement child : children) { - termReplace(out, child, replace, stringToReplaceWith); + termReplace(child, replace, stringToReplaceWith, out); } } } @@ -317,22 +299,29 @@ public class ReplaceIfWithSwitchIntention extends Intention { return values; } - private static void dumpBranch(StringBuilder switchStatementText, - List comments, - List labels, - List statementComments, - PsiStatement body, - boolean wrap, boolean renameBreaks, - String breakLabelName) { - dumpComments(switchStatementText, comments); - dumpLabels(switchStatementText, labels); - dumpComments(switchStatementText, statementComments); - dumpBody(switchStatementText, body, wrap, renameBreaks, - breakLabelName); + private static void dumpBranch(IfStatementBranch branch, + boolean wrap, + boolean renameBreaks, + String breakLabelName, + StringBuilder switchStatementText) { + dumpComments(branch.getComments(), switchStatementText); + if (branch.isElse()) { + switchStatementText.append("default: "); + } else { + for (String label : branch.getConditions()) { + switchStatementText.append("case "); + switchStatementText.append(label); + switchStatementText.append(": "); + } + } + dumpComments(branch.getStatementComments(), switchStatementText); + dumpBody(branch.getStatement(), wrap, renameBreaks, breakLabelName, + switchStatementText + ); } - private static void dumpComments(StringBuilder switchStatementText, - List comments) { + private static void dumpComments(List comments, + StringBuilder switchStatementText) { if (!comments.isEmpty()) { switchStatementText.append('\n'); for (String comment : comments) { @@ -342,30 +331,11 @@ public class ReplaceIfWithSwitchIntention extends Intention { } } - private static void dumpDefaultBranch( - @NonNls StringBuilder switchStatementString, - List comments, PsiStatement body, - List statementComments, boolean wrap, - boolean renameBreaks, String breakLabelName) { - dumpComments(switchStatementString, comments); - switchStatementString.append("default: "); - dumpComments(switchStatementString, statementComments); - dumpBody(switchStatementString, body, wrap, renameBreaks, - breakLabelName); - } - - private static void dumpLabels(@NonNls StringBuilder switchStatementText, - List labels) { - for (String label : labels) { - switchStatementText.append("case "); - switchStatementText.append(label); - switchStatementText.append(": "); - } - } - - private static void dumpBody(@NonNls StringBuilder switchStatementText, - PsiStatement bodyStatement, boolean wrap, - boolean renameBreaks, String breakLabelName) { + private static void dumpBody(PsiStatement bodyStatement, + boolean wrap, + boolean renameBreaks, + String breakLabelName, + @NonNls StringBuilder switchStatementText) { if (wrap) { switchStatementText.append('{'); } @@ -376,12 +346,14 @@ public class ReplaceIfWithSwitchIntention extends Intention { //skip the first and last members, to unwrap the block for (int i = 1; i < children.length - 1; i++) { final PsiElement child = children[i]; - appendElement(switchStatementText, child, renameBreaks, - breakLabelName); + appendElement(child, renameBreaks, breakLabelName, + switchStatementText + ); } } else { - appendElement(switchStatementText, bodyStatement, - renameBreaks, breakLabelName); + appendElement(bodyStatement, renameBreaks, breakLabelName, + switchStatementText + ); } if (ControlFlowUtils.statementMayCompleteNormally( bodyStatement)) { @@ -392,10 +364,10 @@ public class ReplaceIfWithSwitchIntention extends Intention { } } - private static void appendElement( - @NonNls StringBuilder switchStatementText, - PsiElement element, boolean renameBreakElements, - String breakLabelString) { + private static void appendElement(PsiElement element, + boolean renameBreakElements, + String breakLabelString, + @NonNls StringBuilder switchStatementText) { final String text = element.getText(); if (!renameBreakElements) { switchStatementText.append(text); @@ -416,8 +388,8 @@ public class ReplaceIfWithSwitchIntention extends Intention { element instanceof PsiIfStatement) { final PsiElement[] children = element.getChildren(); for (final PsiElement child : children) { - appendElement(switchStatementText, child, renameBreakElements, - breakLabelString); + appendElement(child, renameBreakElements, breakLabelString, + switchStatementText); } } else { switchStatementText.append(text); From 76ff5325b4569abd3de2b7cfcd9dc6cda1bb3716 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Sun, 20 Feb 2011 09:14:00 +0100 Subject: [PATCH 37/38] cleanup --- .../siyeh/ig/controlflow/IfCanBeSwitchInspection.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java index 0a03f433fd0f..1c4a722745c5 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java @@ -418,14 +418,7 @@ public class IfCanBeSwitchInspection extends BaseInspection { switchStatementText.append(breakLabelString); switchStatementText.append(';'); } else { - final String identifierText = identifier.getText(); - if ("".equals(identifierText)) { - switchStatementText.append("break "); - switchStatementText.append(breakLabelString); - switchStatementText.append(';'); - } else { - switchStatementText.append(text); - } + switchStatementText.append(text); } } else if (element instanceof PsiBlockStatement || element instanceof PsiCodeBlock || From f9a615ee35bff1a513e5ccd6da75e3c64d78e3c0 Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 21 Feb 2011 10:33:22 +0300 Subject: [PATCH 38/38] VCS: platform vcs detector: invoke later always --- .../src/com/intellij/platform/PlatformVcsDetector.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/vcs-impl/src/com/intellij/platform/PlatformVcsDetector.java b/platform/vcs-impl/src/com/intellij/platform/PlatformVcsDetector.java index 82604dd6c195..39a37e9213a6 100644 --- a/platform/vcs-impl/src/com/intellij/platform/PlatformVcsDetector.java +++ b/platform/vcs-impl/src/com/intellij/platform/PlatformVcsDetector.java @@ -15,10 +15,12 @@ */ package com.intellij.platform; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.VcsDirectoryMapping; import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; @@ -42,7 +44,7 @@ public class PlatformVcsDetector implements ProjectComponent { public void projectOpened() { StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() { public void run() { - AppUIUtil.invokeLaterIfProjectAlive(myProject, new DumbAwareRunnable() { + final DumbAwareRunnable runnable = new DumbAwareRunnable() { @Override public void run() { VirtualFile file = ProjectBaseDirectory.getInstance(myProject).getBaseDir(myProject.getBaseDir()); @@ -56,6 +58,12 @@ public class PlatformVcsDetector implements ProjectComponent { } } } + }; + ApplicationManager.getApplication().invokeLater(runnable, new Condition() { + @Override + public boolean value(Object o) { + return (! myProject.isOpen()) || myProject.isDisposed(); + } }); } });