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; 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..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 @@ -13,14 +13,10 @@ * 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.Lists; +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 +56,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; @@ -853,14 +853,23 @@ 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; + 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) { @@ -874,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) { @@ -886,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; @@ -902,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-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-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/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 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/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/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/codeInsight/daemon/LightAdvHighlightingJdk7Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java index dcb332113c87..6b64c70d82de 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,15 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase { public void testMultiCatch() throws Exception { doTest(false, false); } - + + public void testMultiCatchWarn() throws Exception { + doTest(true, false); + } + + public void testTryWithResources() throws Exception { + doTest(false, false); + } + public void testSafeVarargsApplicability() throws Exception { doTest(true, false); } 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/projectView/NavigateFromSourceTest.java b/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java index d3597d29cf4b..6e836aaed54c 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"); @@ -82,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"); 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/mockJDK-1.7/jre/lib/rt.jar b/java/mockJDK-1.7/jre/lib/rt.jar index 6009cb2194d5..4bc199ad9ad4 100644 Binary files a/java/mockJDK-1.7/jre/lib/rt.jar and b/java/mockJDK-1.7/jre/lib/rt.jar differ diff --git a/java/mockJDK-1.7/src.zip b/java/mockJDK-1.7/src.zip index 4ac79dc0a0bd..57bfb2c5cfb9 100644 Binary files a/java/mockJDK-1.7/src.zip and b/java/mockJDK-1.7/src.zip differ 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/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/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/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index 11e5bafcc6eb..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 @@ -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); @@ -1187,6 +1193,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { c.gridy = i; c.ipadx = itemTextPadding; c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1.0; { @@ -1239,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); @@ -1249,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); 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(); + } + } +} 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/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/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/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(); } 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); } 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(); } 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/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()); } } } 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 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) { 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: 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(); + } }); } }); diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index f584e79b4ab1..8cad05468417 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1847,3 +1847,7 @@ 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' +if.can.be.switch.minimum.branch.option=Minimum number of branches 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..1c4a722745c5 --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/IfCanBeSwitchInspection.java @@ -0,0 +1,459 @@ +/* + * 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.codeInspection.ui.SingleIntegerFieldOptionsPanel; +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.InspectionGadgetsBundle; +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 javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +public class IfCanBeSwitchInspection extends BaseInspection { + + @SuppressWarnings({"PublicField"}) + public int minimumBranches = 3; + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("if.can.be.switch.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "if.can.be.switch.problem.descriptor"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + 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; + + public IfCanBeSwitchFix(PsiExpression switchExpression) { + this.switchExpression = switchExpression; + } + + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("if.can.be.switch.quickfix"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) + throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement().getParent(); + 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("){"); + for (IfStatementBranch branch : branches) { + boolean hasConflicts = false; + for (IfStatementBranch testBranch : branches) { + if (branch == testBranch) { + continue; + } + if (branch.topLevelDeclarationsConflictWith(testBranch)) { + hasConflicts = true; + } + } + dumpBranch(branch, hasConflicts, breaksNeedRelabeled, + labelString, switchStatementText); + } + 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( + 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 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( + PsiStatement bodyStatement, boolean wrap, boolean renameBreaks, + String breakLabelName, + @NonNls StringBuilder switchStatementText) { + 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 { + 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 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, minimumBranches); + 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/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( 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..e319ad78ca59 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,247 @@ public class SwitchUtils{ } return branches; } + + @Nullable + public static PsiExpression getSwitchExpression(PsiIfStatement statement, + int minimumBranches){ + 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; + } + int branchCount = 0; + while(true){ + branchCount++; + final PsiExpression caseCondition = statement.getCondition(); + if (!canBeMadeIntoCase(caseCondition, possibleSwitchExpression, + languageLevel)) { + break; + } + final PsiStatement elseBranch = statement.getElseBranch(); + if(!(elseBranch instanceof PsiIfStatement)){ + if (elseBranch != null) { + branchCount++; + } + if (branchCount < minimumBranches) { + return null; + } + 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..6fb425c8f6bc --- /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 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}'' 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) { 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..16848a9d3584 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){ @@ -163,22 +171,22 @@ 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; private BreakTargetFinder(PsiStatement target){ - super(); m_target = target; } @@ -186,6 +194,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){ } @@ -203,7 +219,7 @@ public class ControlFlowUtils{ } } - private static class ExitingBreakFinder + private static class NakedBreakFinder extends JavaRecursiveElementWalkingVisitor{ private boolean m_found = false; @@ -212,7 +228,16 @@ public class ControlFlowUtils{ return m_found; } - @Override public void visitReferenceExpression(PsiReferenceExpression expression){ + @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){ @@ -222,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 } @@ -230,11 +256,17 @@ 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 } - @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/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/IfToSwitchPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/IfToSwitchPredicate.java index 54d148bbdbc4..d0698ada3846 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 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 48577726b859..c35f86c1ce6a 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,20 +17,18 @@ 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; 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 { @@ -51,51 +49,44 @@ public class ReplaceIfWithSwitchIntention extends Intention { boolean breaksNeedRelabeled = false; 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(); - } + if (ControlFlowUtils.statementContainsNakedBreak(ifStatement)) { + 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 = SwitchUtils.findUniqueLabelName(ifStatement, + "label"); + breaksNeedRelabeled = true; + } } } final PsiIfStatement statementToReplace = ifStatement; - final PsiExpression caseExpression = - CaseUtil.getCaseExpression(ifStatement); - assert caseExpression != null; + final PsiExpression switchExpression = + SwitchUtils.getSwitchExpression(ifStatement); + assert switchExpression != null; 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, + getValuesFromExpression(condition, switchExpression, 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 = @@ -116,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; } @@ -144,36 +127,20 @@ public class ReplaceIfWithSwitchIntention extends Intention { @NonNls final StringBuilder switchStatementText = new StringBuilder(); switchStatementText.append("switch("); - switchStatementText.append(caseExpression.getText()); - switchStatementText.append(')'); - switchStatementText.append('{'); + switchStatementText.append(switchExpression.getText()); + switchStatementText.append("){"); 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; } } - - 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 = @@ -181,10 +148,12 @@ public class ReplaceIfWithSwitchIntention extends Intention { final PsiElementFactory factory = psiFacade.getElementFactory(); if (breaksNeedRelabeled) { final StringBuilder out = new StringBuilder(); - out.append(labelString); - out.append(':'); - termReplace(out, breakTarget, statementToReplace, - switchStatementText); + if (!(breakTarget instanceof PsiLabeledStatement)) { + out.append(labelString); + out.append(':'); + } + termReplace(breakTarget, statementToReplace, switchStatementText, + out); final String newStatementText = out.toString(); final PsiStatement newStatement = factory.createStatementFromText(newStatementText, element); @@ -266,17 +235,16 @@ 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) { - final String text = target.getText(); - out.append(text); + out.append(target.getText()); } else { final PsiElement[] children = target.getChildren(); for (final PsiElement child : children) { - termReplace(out, child, replace, stringToReplaceWith); + termReplace(child, replace, stringToReplaceWith, out); } } } @@ -322,128 +290,109 @@ 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, - 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, - 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 switchStatementString, - List comments) { + private static void dumpComments(List comments, + StringBuilder switchStatementText) { 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'); } } } - 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 switchStatementString, - List labels) { - for (String label : labels) { - switchStatementString.append("case "); - switchStatementString.append(label); - switchStatementString.append(": "); + private static void dumpBody(PsiStatement bodyStatement, + boolean wrap, + boolean renameBreaks, + String breakLabelName, + @NonNls StringBuilder switchStatementText) { + if (wrap) { + switchStatementText.append('{'); } - } - - private static void dumpBody(@NonNls StringBuilder switchStatementString, - PsiStatement bodyStatement, boolean wrap, - boolean renameBreaks, String breakLabelName) { if (bodyStatement instanceof PsiBlockStatement) { - if (wrap) { - appendElement(switchStatementString, 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(switchStatementString, 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(child, renameBreaks, breakLabelName, + switchStatementText + ); } } else { - if (wrap) { - switchStatementString.append('{'); - appendElement(switchStatementString, bodyStatement, - renameBreaks, breakLabelName); - switchStatementString.append('}'); - } else { - appendElement(switchStatementString, bodyStatement, - renameBreaks, breakLabelName); - } + appendElement(bodyStatement, renameBreaks, breakLabelName, + switchStatementText + ); } - if (ControlFlowUtils.statementMayCompleteNormally(bodyStatement)) { - switchStatementString.append("break; "); + if (ControlFlowUtils.statementMayCompleteNormally( + bodyStatement)) { + switchStatementText.append("break;"); + } + if (wrap) { + switchStatementText.append('}'); } } - private static void appendElement( - @NonNls StringBuilder switchStatementString, - 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) { - 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, - breakLabelString); + appendElement(child, renameBreakElements, breakLabelString, + switchStatementText); } } 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..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 (CaseUtil.containsHiddenBreak(statements[i])) { - renameBreaks = true; - break; - } - } - final List openBranches = new ArrayList(); final Set declaredVariables = @@ -161,12 +152,6 @@ public class ReplaceSwitchWithIfIntention extends Intention { } } final StringBuilder ifStatementText = new StringBuilder(); - String breakLabel = null; - if (renameBreaks) { - breakLabel = CaseUtil.findUniqueLabel(switchStatement, "Label"); - ifStatementText.append(breakLabel); - ifStatementText.append(':'); - } boolean firstBranch = true; SwitchStatementBranch defaultBranch = null; for (SwitchStatementBranch branch : allBranches) { @@ -177,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; } @@ -188,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(); @@ -244,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( @@ -291,13 +273,12 @@ 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 (CaseUtil.isUsedByStatementList(variable, bodyStatements)) { + if (SwitchUtils.isUsedByStatementList(variable, bodyStatements)) { final PsiType varType = variable.getType(); ifStatementString.append(varType.getPresentableText()); ifStatementString.append(' '); @@ -306,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 diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/CaseUtil.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/SwitchUtils.java similarity index 61% rename from plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/CaseUtil.java rename to plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/SwitchUtils.java index 54e748877077..a9fb0aaff79d 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/CaseUtil.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/switchtoif/SwitchUtils.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; @@ -26,69 +27,29 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -class CaseUtil{ +class SwitchUtils { - private CaseUtil(){ - super(); - } + private SwitchUtils(){} - 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 +70,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 +94,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 +131,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 +148,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 +168,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 +204,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 +228,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/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); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic b/plugins/spellchecker/src/com/intellij/spellchecker/jetbrains.dic index 5e4a963686c2..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 @@ -314,6 +315,7 @@ versa vertices webservice whitespace +whitespaces wildcard workflow wsdl diff --git a/resources-en/src/messages/JavaErrorMessages.properties b/resources-en/src/messages/JavaErrorMessages.properties index c7d72c56649a..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 @@ -310,6 +311,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