Merge branch 'master' of git.labs.intellij.net:idea/community

This commit is contained in:
Maxim Medvedev
2011-02-21 11:48:30 +03:00
97 changed files with 3076 additions and 1889 deletions
@@ -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();
}
@@ -48,8 +48,6 @@ public class CompilerParsingThread implements Runnable, OutputParser.Callback {
private volatile boolean myProcessExited = false;
private final CompileContext myContext;
//private final BlockingQueue<String> myLines = new LinkedBlockingQueue<String>();
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;
@@ -338,9 +338,9 @@ public class ExceptionUtil {
@NotNull
private static List<PsiClassType> 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
@@ -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<String, Set<String>> ourInterfaceIncompatibleModifiers;
@@ -853,14 +853,23 @@ public class HighlightUtil {
@Nullable
static HighlightInfo checkExceptionThrownInTry(final PsiParameter parameter) {
static Collection<HighlightInfo> checkExceptionThrownInTry(final PsiParameter parameter) {
final PsiElement declarationScope = parameter.getDeclarationScope();
if (!(declarationScope instanceof PsiCatchSection)) return null;
final Set<PsiClassType> thrownTypes = Sets.newHashSet();
final PsiTryStatement statement = ((PsiCatchSection)declarationScope).getTryStatement();
final PsiCodeBlock tryBlock = statement.getTryBlock();
assert tryBlock != null : statement;
final Collection<PsiClassType> 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<PsiClassType> thrownTypes,
final PsiClassType caughtType) {
private static Collection<HighlightInfo> checkSimpleCatchParameter(final PsiParameter parameter,
final Collection<PsiClassType> 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<PsiClassType> thrownTypes) {
for (PsiTypeElement typeElement : PsiTreeUtil.getChildrenOfTypeAsList(parameter.getTypeElement(), PsiTypeElement.class)) {
private static Collection<HighlightInfo> checkMultiCatchParameter(final PsiParameter parameter,
final Collection<PsiClassType> thrownTypes) {
final List<PsiTypeElement> typeElements = PsiTreeUtil.getChildrenOfTypeAsList(parameter.getTypeElement(), PsiTypeElement.class);
final Collection<HighlightInfo> 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;
}
@@ -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));
}
@@ -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<PsiBuilder.Marker, Boolean> 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
@@ -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);
@@ -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"));
}
@@ -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;
@@ -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 + "\".");
@@ -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;
}
}
@@ -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";
}
@@ -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<UsageInfo> 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) {
@@ -1,7 +1,7 @@
import pack.AClass;
import pack.AAClass;
public class Test1 {
public void foo() {
new AClass()<caret>
new AAClass()<caret>
}
}
@@ -1,5 +1,5 @@
public class Test1 {
public void foo() {
new ACl<caret>
new AACl<caret>
}
}
@@ -0,0 +1,4 @@
package pack;
public class AAClass {
}
@@ -1,4 +0,0 @@
package pack;
public class AClass{
}
@@ -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<T> { }
private static class IE1 extends E implements I<Integer> { }
@@ -23,7 +24,7 @@ abstract class C {
try { g(); } catch (IE1 | IE2 e) { new F<I<? extends Number>>(e); }
try { f(); } catch (E1 | E2 | <error descr="Exception 'C.E3' is never thrown in the corresponding try block">E3</error> e) { }
try { f(); } catch (<error descr="Exception 'C.E3' is never thrown in the corresponding try block">E3</error> | E e) { }
try { f(); } catch (<error descr="Exception 'C.E3' is never thrown in the corresponding try block">E3</error> | <error descr="Exception 'C.E4' is never thrown in the corresponding try block">E4</error> | E e) { }
try { f(); } catch (E | <error descr="Exception 'C.E1' has already been caught">E1</error> e) { }
try { f(); } catch (E | <error descr="Exception 'C.E3' has already been caught">E3</error> e) { }
@@ -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 (<warning descr="Exception 'C.E1' is also caught by 'C.E'">E1</warning> | E ignore) { }
}
}
@@ -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) {
<error descr="Cannot resolve symbol 'reader'">reader</error> = null;
}
<error descr="Cannot resolve symbol 'reader'">reader</error> = 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();
}
}*/
}
@@ -0,0 +1,10 @@
PsiJavaFile:TryIncomplete10.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(
PsiJavaToken:LPARENTH('(')
PsiErrorElement:')' expected
<empty list>
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,11 @@
PsiJavaFile:TryIncomplete11.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:()
PsiJavaToken:LPARENTH('(')
PsiErrorElement:Resource definition expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,14 @@
PsiJavaFile:TryIncomplete12.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(;)
PsiJavaToken:LPARENTH('(')
PsiErrorElement:Resource definition expected
<empty list>
PsiJavaToken:SEMICOLON(';')
PsiErrorElement:Resource definition expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,18 @@
PsiJavaFile:TryIncomplete13.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(final )
PsiJavaToken:LPARENTH('(')
PsiModifierList:final
PsiKeyword:final('final')
PsiErrorElement:Type expected
<empty list>
PsiWhiteSpace(' ')
PsiTypeElement:
<empty list>
PsiErrorElement:Identifier expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,18 @@
PsiJavaFile:TryIncomplete14.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(R)
PsiJavaToken:LPARENTH('(')
PsiModifierList:
<empty list>
PsiTypeElement:R
PsiJavaCodeReferenceElement:R
PsiIdentifier:R('R')
PsiReferenceParameterList
<empty list>
PsiErrorElement:Identifier expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,21 @@
PsiJavaFile:TryIncomplete15.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(R r)
PsiJavaToken:LPARENTH('(')
PsiParameter:r
PsiModifierList:
<empty list>
PsiTypeElement:R
PsiJavaCodeReferenceElement:R
PsiIdentifier:R('R')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:r('r')
PsiErrorElement:'=' expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,23 @@
PsiJavaFile:TryIncomplete16.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(R r =)
PsiJavaToken:LPARENTH('(')
PsiParameter:r
PsiModifierList:
<empty list>
PsiTypeElement:R
PsiJavaCodeReferenceElement:R
PsiIdentifier:R('R')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:r('r')
PsiWhiteSpace(' ')
PsiJavaToken:EQ('=')
PsiErrorElement:Expression expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,27 @@
PsiJavaFile:TryIncomplete17.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(R r = 0;)
PsiJavaToken:LPARENTH('(')
PsiParameter:r
PsiModifierList:
<empty list>
PsiTypeElement:R
PsiJavaCodeReferenceElement:R
PsiIdentifier:R('R')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:r('r')
PsiWhiteSpace(' ')
PsiJavaToken:EQ('=')
PsiWhiteSpace(' ')
PsiLiteralExpression:0
PsiJavaToken:INTEGER_LITERAL('0')
PsiJavaToken:SEMICOLON(';')
PsiErrorElement:Identifier or type expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,25 @@
PsiJavaFile:TryIncomplete8.java
PsiTryStatement
PsiKeyword:try('try')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
PsiCatchSection
PsiKeyword:catch('catch')
PsiJavaToken:LPARENTH('(')
PsiModifierList:
<empty list>
PsiTypeElement:A|
PsiTypeElement:A
PsiJavaCodeReferenceElement:A
PsiIdentifier:A('A')
PsiReferenceParameterList
<empty list>
PsiJavaToken:OR('|')
PsiErrorElement:Identifier expected
<empty list>
PsiErrorElement:Identifier expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiErrorElement:'{' expected
<empty list>
@@ -0,0 +1,28 @@
PsiJavaFile:TryIncomplete9.java
PsiTryStatement
PsiKeyword:try('try')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
PsiCatchSection
PsiKeyword:catch('catch')
PsiJavaToken:LPARENTH('(')
PsiModifierList:
<empty list>
PsiTypeElement:A|B
PsiTypeElement:A
PsiJavaCodeReferenceElement:A
PsiIdentifier:A('A')
PsiReferenceParameterList
<empty list>
PsiJavaToken:OR('|')
PsiTypeElement:B
PsiJavaCodeReferenceElement:B
PsiIdentifier:B('B')
PsiReferenceParameterList
<empty list>
PsiErrorElement:Identifier expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiErrorElement:'{' expected
<empty list>
@@ -8,8 +8,9 @@ PsiJavaFile:TryNormal1.java
PsiKeyword:catch('catch')
PsiJavaToken:LPARENTH('(')
PsiParameter:e
PsiModifierList:
<empty list>
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('}')
PsiJavaToken:RBRACE('}')
@@ -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:
<empty list>
PsiTypeElement:A|B
PsiTypeElement:A
PsiJavaCodeReferenceElement:A
PsiIdentifier:A('A')
PsiReferenceParameterList
<empty list>
PsiJavaToken:OR('|')
PsiTypeElement:B
PsiJavaCodeReferenceElement:B
PsiIdentifier:B('B')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:e('e')
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,24 @@
PsiJavaFile:TryNormal4.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(R r = 0)
PsiJavaToken:LPARENTH('(')
PsiParameter:r
PsiModifierList:
<empty list>
PsiTypeElement:R
PsiJavaCodeReferenceElement:R
PsiIdentifier:R('R')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:r('r')
PsiWhiteSpace(' ')
PsiJavaToken:EQ('=')
PsiWhiteSpace(' ')
PsiLiteralExpression:0
PsiJavaToken:INTEGER_LITERAL('0')
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,41 @@
PsiJavaFile:TryNormal5.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(R1 r1 = 1; R2 r2 = 2)
PsiJavaToken:LPARENTH('(')
PsiParameter:r1
PsiModifierList:
<empty list>
PsiTypeElement:R1
PsiJavaCodeReferenceElement:R1
PsiIdentifier:R1('R1')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:r1('r1')
PsiWhiteSpace(' ')
PsiJavaToken:EQ('=')
PsiWhiteSpace(' ')
PsiLiteralExpression:1
PsiJavaToken:INTEGER_LITERAL('1')
PsiJavaToken:SEMICOLON(';')
PsiWhiteSpace(' ')
PsiParameter:r2
PsiModifierList:
<empty list>
PsiTypeElement:R2
PsiJavaCodeReferenceElement:R2
PsiIdentifier:R2('R2')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:r2('r2')
PsiWhiteSpace(' ')
PsiJavaToken:EQ('=')
PsiWhiteSpace(' ')
PsiLiteralExpression:2
PsiJavaToken:INTEGER_LITERAL('2')
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -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);
}
@@ -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);
}
});
}
}
@@ -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");
@@ -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();
Binary file not shown.
Binary file not shown.
@@ -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();
}
@@ -46,5 +46,6 @@ public interface SmartPsiElementPointer<E extends PsiElement> {
VirtualFile getVirtualFile();
@Nullable
Segment getRange();
}
@@ -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;
@@ -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;
@@ -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);
@@ -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<LookupElement> 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);
@@ -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<Boolean> 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();
}
}
@@ -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) ;
}
}
@@ -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();
}
@@ -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<RangeHighlighter> myHighlighters = new HashSet<RangeHighlighter>();
private RangeHighlighter myCursorHighlighter;
private final List<VisibleAreaListener> myVisibleAreaListenersToRemove = new ArrayList<VisibleAreaListener>();
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<CursorListener> myListeners = new ArrayList<CursorListener>();
public void addCursorListener(CursorListener listener) {
myListeners.add(listener);
}
public void removeCursorListener(CursorListener listener) {
myListeners.remove(listener);
}
public interface Delegate {
@NotNull
List<LiveOccurrence> 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<Boolean> 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<LiveOccurrence> getSearchResults() {
return mySearchResults;
}
private List<LiveOccurrence> 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<Boolean> needToUpdate = new Ref<Boolean>();
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<LiveOccurrence>() {
@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<LiveOccurrence> performSearchInBackground() {
final AtomicReference<List<LiveOccurrence>> occurrences = new AtomicReference<List<LiveOccurrence>>();
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<RangeHighlighter> dummy = new ArrayList<RangeHighlighter>();
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<Balloon>(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<RangeHighlighter> 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);
}
}
}
@@ -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<LiveOccurrence> 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<Boolean> 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();
}
}
@@ -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 = "<Empty string>";
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<FindResult> results, Collection<LiveOccurrence> occurrences) {
for (FindResult r : results) {
LiveOccurrence occurrence = new LiveOccurrence();
occurrence.setPrimaryRange(r);
occurrences.add(occurrence);
}
}
@NotNull
@Override
public List<LiveOccurrence> performSearchInBackgroundInReadAction(Editor editor) {
ArrayList<LiveOccurrence> occurrences = new ArrayList<LiveOccurrence>();
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<FindResult> results = new ArrayList<FindResult>();
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<Pair<TextRange, String>>());
}
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<Boolean> needToUpdate) {
return 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));
}
}
}
@@ -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();
}
}
@@ -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<RangeHighlighter> myHighlighters = new HashSet<RangeHighlighter>();
private RangeHighlighter myCursorHighlighter;
private final List<VisibleAreaListener> myVisibleAreaListenersToRemove = new ArrayList<VisibleAreaListener>();
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<RangeHighlighter> dummy = new ArrayList<RangeHighlighter>();
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<RangeHighlighter> 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<Balloon> {
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);
}
}
}
@@ -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 = "<Empty string>";
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<Pair<TextRange, String>>());
}
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;
}
}
@@ -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();
}
}
@@ -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<SearchResultsListener> myListeners = new ArrayList<SearchResultsListener>();
private LiveOccurrence myCursor;
private List<LiveOccurrence> myOccurrences = new ArrayList<LiveOccurrence>();
private Set<LiveOccurrence> myExcluded = new HashSet<LiveOccurrence>();
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<LiveOccurrence> 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<LiveOccurrence> 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<FindResult> results, Collection<LiveOccurrence> occurrences) {
for (FindResult r : results) {
LiveOccurrence occurrence = new LiveOccurrence();
occurrence.setPrimaryRange(r);
occurrences.add(occurrence);
}
}
public void updateThreadSafe(final FindModel findModel) {
final ArrayList<LiveOccurrence> occurrences = new ArrayList<LiveOccurrence>();
final Editor editor = getEditor();
final ArrayList<FindResult> results = new ArrayList<FindResult>();
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<LiveOccurrence> 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<LiveOccurrence>() {
@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();
}
}
}
@@ -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;
}
@@ -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;
@@ -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<PsiDirectory> dirs =
ContainerUtil
.mapNotNull(ProjectRootManager.getInstance(project).getContentSourceRoots(), new Function<VirtualFile, PsiDirectory>() {
@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<PsiDirectory> dirs =
ContainerUtil
.mapNotNull(ProjectRootManager.getInstance(project).getContentSourceRoots(), new Function<VirtualFile, PsiDirectory>() {
@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;
@@ -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<FormatterUtilHelper> ourHelpers = ContainerUtil.createEmptyCOWList();
public static final Collection<String> FORMATTER_ACTION_NAMES = Collections.unmodifiableCollection(ContainerUtil.addAll(
new HashSet<String>(), ReformatAndOptimizeImportsProcessor.COMMAND_NAME, ReformatCodeProcessor.COMMAND_NAME
));
private FormatterUtil() {
}
@@ -41,6 +41,8 @@ interface SmartPointerElementInfo {
boolean pointsToTheSameElementAs(SmartPointerElementInfo other);
VirtualFile getVirtualFile();
@Nullable
Segment getRange();
@NotNull Project getProject();
}
@@ -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;
@@ -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<UsageInfo> results,
@NotNull final Collection<UsageInfo> results,
@NotNull final UsageInfoFactory factory) {
processTextOccurences(element, stringToSearch, searchScope, new Processor<UsageInfo>() {
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<UsageInfo> results,
@NotNull final Collection<UsageInfo> results,
@NotNull final UsageInfoFactory factory) {
processUsagesInStringsAndComments(element, stringToSearch, false, new PairProcessor<PsiElement, TextRange>() {
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<UsageInfo> 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);
}
@@ -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) {
@@ -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<FeatureStatisticsBundleProvider> EP_NAME = new ExtensionPointName<FeatureStatisticsBundleProvider>("com.intellij.featureStatBundle");
@Nullable
ResourceBundle getBundleFor(String key);
ResourceBundle getBundle();
}
@@ -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";
@@ -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() {
@@ -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());
}
}
}
@@ -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
@@ -478,11 +478,12 @@ public class ContainerUtil {
}
}
public static <T> void addAll(@NotNull Collection<T> collection, @NotNull T... elements) {
public static <T> Collection<T> addAll(@NotNull Collection<T> collection, @NotNull T... elements) {
//noinspection ManualArrayToCollectionCopy
for (T element : elements) {
collection.add(element);
}
return collection;
}
public static <T, U extends T> U findInstance(@NotNull Iterable<T> iterable, @NotNull Class<U> aClass) {
@@ -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:
@@ -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();
}
});
}
});
@@ -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=<code>#ref</code> statement replaceable with 'switch' statement
if.can.be.switch.quickfix=Replace with 'switch'
if.can.be.switch.minimum.branch.option=Minimum number of branches
@@ -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);
@@ -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<IfStatementBranch> branches =
new ArrayList<IfStatementBranch>(20);
while (true) {
final PsiExpression condition = ifStatement.getCondition();
final List<PsiExpression> 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 extends PsiElement> T getPrevSiblingOfType(
@Nullable PsiElement element,
@NotNull Class<T> aClass,
@NotNull Class<? extends PsiElement>... stopAt) {
if (element == null) {
return null;
}
PsiElement sibling = element.getPrevSibling();
while (sibling != null && !aClass.isInstance(sibling)) {
for (Class<? extends PsiElement> 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<PsiExpression> getValuesFromExpression(
PsiExpression expression, PsiExpression caseExpression,
List<PsiExpression> 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<String> 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);
}
}
}
@@ -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<String> topLevelVariables = new HashSet<String>(3);
private final LinkedList<String> comments = new LinkedList<String>();
private final LinkedList<String> statementComments = new LinkedList<String>();
private final List<String> conditions = new ArrayList<String>(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<String> getConditions(){
return Collections.unmodifiableList(conditions);
}
public boolean isElse(){
return elseBranch;
}
public boolean topLevelDeclarationsConflictWith(
IfStatementBranch testBranch){
final Set<String> topLevel = testBranch.topLevelVariables;
return intersects(topLevelVariables, topLevel);
}
private static boolean intersects(Set<String> set1,
Set<String> set2){
for(final String s : set1){
if(set2.contains(s)){
return true;
}
}
return false;
}
public List<String> getComments() {
return comments;
}
public List<String> 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);
}
}
}
}
@@ -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(
@@ -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(
@@ -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(
@@ -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) {
@@ -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;
}
}
}
@@ -0,0 +1,10 @@
<html>
<body>
This inspection reports any <b>if</b> statements with which can be replaced
by a <b>switch</b> 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 <b>case</b> branches the resulting <b>switch</b> statement should have.
<small>New in 11?, Powered by InspectionGadgets</small>
</body>
</html>
@@ -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}''
@@ -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) {
@@ -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(
@@ -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
}
}
@@ -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<String> variablesDeclaredAtTopLevel,
Set<String> 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<String> variablesDeclaredAtTopLevel,
Set<String>variablesDeclaredAtLowerLevels, boolean isTopLevel){
if(block == null){
return;
}
final PsiStatement[] statements = block.getStatements();
for(PsiStatement statement : statements){
calculateVariablesDeclared(
statement, variablesDeclaredAtTopLevel,
variablesDeclaredAtLowerLevels, isTopLevel);
}
}
}
@@ -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<String> m_topLevelVariables = new HashSet<String>(3);
private Set<String> m_innerVariables = new HashSet<String>(3);
private final Set<String> topLevelVariables = new HashSet<String>(3);
private final LinkedList<String> comments = new LinkedList<String>();
private final LinkedList<String> statementComments = new LinkedList<String>();
private final List<String> m_conditions = new ArrayList<String>(3);
private PsiStatement m_statement = null;
private boolean m_else = false;
private final List<String> conditions = new ArrayList<String>(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<String> 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<String> topLevelVariables){
m_topLevelVariables = new HashSet<String>(topLevelVariables);
}
public void setInnerVariables(Set<String> innerVariables){
m_innerVariables = new HashSet<String>(innerVariables);
}
private Set<String> getTopLevelVariables(){
return Collections.unmodifiableSet(m_topLevelVariables);
}
private Set<String> getInnerVariables(){
return Collections.unmodifiableSet(m_innerVariables);
}
public boolean topLevelDeclarationsConfictWith(
public boolean topLevelDeclarationsConflictWith(
IfStatementBranch testBranch){
final Set<String> innerVariables = testBranch.getInnerVariables();
final Set<String> topLevel = testBranch.getTopLevelVariables();
return hasNonEmptyIntersection(m_topLevelVariables, topLevel) ||
hasNonEmptyIntersection(m_topLevelVariables,
innerVariables);
final Set<String> topLevel = testBranch.topLevelVariables;
return intersects(topLevelVariables, topLevel);
}
private static boolean hasNonEmptyIntersection(Set<String> set1,
Set<String> set2){
for(final String set1Element : set1){
if(set2.contains(set1Element)){
private static boolean intersects(Set<String> set1,
Set<String> set2){
for(final String s : set1){
if(set2.contains(s)){
return true;
}
}
@@ -106,4 +81,28 @@ class IfStatementBranch{
public List<String> 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);
}
}
}
}
@@ -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;
}
}
@@ -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<IfStatementBranch> branches =
new ArrayList<IfStatementBranch>(20);
while (true) {
final Set<String> topLevelVariables = new HashSet<String>(5);
final Set<String> innerVariables = new HashSet<String>(5);
final PsiExpression condition = ifStatement.getCondition();
final List<PsiExpression> 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<String> elseTopLevelVariables = new HashSet<String>(5);
final Set<String> elseInnerVariables = new HashSet<String>(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<String> comments = branch.getComments();
final List<String> statementComments =
branch.getStatementComments();
dumpDefaultBranch(switchStatementText, comments,
branchStatement, statementComments,
hasConflicts,
breaksNeedRelabeled, labelString);
} else {
final List<String> conditions = branch.getConditions();
final List<String> comments = branch.getComments();
final List<String> 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<String> comments,
List<String> labels,
List<String> 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<String> comments) {
private static void dumpComments(List<String> 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<String> comments, PsiStatement body,
List<String> 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<String> 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);
}
}
}
@@ -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<SwitchStatementBranch> openBranches =
new ArrayList<SwitchStatementBranch>();
final Set<PsiLocalVariable> 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<PsiElement> bodyElements = branch.getBodyElements();
final Set<PsiLocalVariable> 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<PsiLocalVariable> 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<String> caseValues,
List<PsiElement> bodyStatements, String breakLabel,
List<PsiElement> bodyStatements,
Set<PsiLocalVariable> 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<PsiElement> bodyStatements, String breakLabel,
List<PsiElement> bodyStatements,
Set<PsiLocalVariable> 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<PsiElement> bodyStatements, String breakLabel,
Set<PsiLocalVariable> variables, boolean renameBreaks,
@NonNls StringBuilder ifStatementString) {
private static void dumpBody(List<PsiElement> bodyStatements,
Set<PsiLocalVariable> 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);
}
}
@@ -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;
}
}
@@ -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<VirtualFile, Set<String>> unchecked, final boolean updateTree) {
private void refreshTree(final boolean fetchData, final Map<VirtualFile, Set<String>> unchecked, final boolean updateUncheckedCommits) {
myCommitTree.setPaintBusy(true);
loadRootsInBackground(fetchData, new PushActiveBranchRunnable(){
@Override
void run(List<Root> roots) {
if (updateTree) {
updateTree(roots, unchecked);
}
updateTree(roots, updateUncheckedCommits ? unchecked : null);
updateUI();
myCommitTree.setPaintBusy(false);
}
@@ -40,6 +40,7 @@ classpath
clazz
clob
closable
closeable
cmdline
commandline
config
@@ -314,6 +315,7 @@ versa
vertices
webservice
whitespace
whitespaces
wildcard
workflow
wsdl
@@ -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