diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValueModifier.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValueModifier.java index 16f75368edf9..612c50db271f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValueModifier.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValueModifier.java @@ -22,29 +22,19 @@ import com.intellij.debugger.engine.evaluation.*; import com.intellij.debugger.engine.evaluation.expression.*; import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; -import com.intellij.debugger.impl.*; +import com.intellij.debugger.impl.DebuggerContextImpl; +import com.intellij.debugger.impl.DebuggerSession; +import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.jdi.LocalVariableProxyImpl; import com.intellij.debugger.jdi.VirtualMachineProxyImpl; -import com.intellij.debugger.ui.DebuggerExpressionComboBox; -import com.intellij.debugger.ui.EditorEvaluationCommand; -import com.intellij.debugger.ui.impl.DebuggerTreeRenderer; import com.intellij.debugger.ui.impl.watch.*; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.util.ProgressIndicatorListenerAdapter; import com.intellij.openapi.progress.util.ProgressWindowWithNotification; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.intellij.ui.SimpleColoredComponent; -import com.intellij.util.IJSwingUtilities; import com.intellij.xdebugger.frame.XValueModifier; import com.sun.jdi.*; import org.jetbrains.annotations.NotNull; -import javax.swing.*; - import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING; /* @@ -255,14 +245,14 @@ public class JavaValueModifier extends XValueModifier { } } - private Value preprocessValue(EvaluationContextImpl context, Value value, Type varType) throws EvaluateException { + private static Value preprocessValue(EvaluationContextImpl context, Value value, Type varType) throws EvaluateException { if (value != null && JAVA_LANG_STRING.equals(varType.name()) && !(value instanceof StringReference)) { - String v = DebuggerUtilsEx.getValueAsString(context, value); + String v = DebuggerUtils.getValueAsString(context, value); if (v != null) { value = context.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf(v); } } - if(value instanceof DoubleValue) { + if (value instanceof DoubleValue) { double dValue = ((DoubleValue) value).doubleValue(); if(varType instanceof FloatType && Float.MIN_VALUE <= dValue && dValue <= Float.MAX_VALUE){ value = context.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf((float)dValue); @@ -274,8 +264,7 @@ public class JavaValueModifier extends XValueModifier { value = (Value)new UnBoxingEvaluator(new IdentityEvaluator(value)).evaluate(context); } } - else if (UnBoxingEvaluator.isTypeUnboxable(varType.name())) { - // variable is not primitive and boxing/unboxing is applicable + else if (varType instanceof ReferenceType) { if (value instanceof PrimitiveValue) { value = (Value)new BoxingEvaluator(new IdentityEvaluator(value)).evaluate(context); } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java index 4e9b273a0c03..21c9ccd7965c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java @@ -530,8 +530,19 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio return null; } final int line = Math.min(finalRangeBegin + 1, finalRangeEnd); - final SourcePosition candidatePosition = positionLine == line? position : SourcePosition.createFromLine(position.getFile(), line); - return classToFind.equals(JVMNameUtil.getClassAt(candidatePosition)) ? fromClass : null; + Set lineClasses = getLineClasses(position.getFile(), line); + if (lineClasses.size() > 1) { + // if there's more than one class on the line - try to match by name + for (PsiClass aClass : lineClasses) { + if (classToFind.equals(aClass)) { + return fromClass; + } + } + } + else if (!lineClasses.isEmpty()){ + return classToFind.equals(lineClasses.iterator().next())? fromClass : null; + } + return null; } }); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index d7c7c586890a..aa64c0a469d0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -650,10 +650,12 @@ public class DfaMemoryStateImpl implements DfaMemoryState { return true; } + // track "x" property state only inside "if (getX() != null) ..." + if (dfaLeft instanceof DfaVariableValue && ((DfaVariableValue)dfaLeft).containsCalls() && (!isNull(dfaRight) || !isNegated)) { + return true; + } + if (dfaLeft == dfaRight) { - if (dfaLeft instanceof DfaVariableValue && ((DfaVariableValue)dfaLeft).containsCalls()) { - return true; - } return !isNegated; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java index 189e96bf01f0..f7aa1da08598 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java @@ -27,7 +27,6 @@ package com.intellij.codeInspection.dataFlow.instructions; import com.intellij.codeInspection.dataFlow.*; import com.intellij.codeInspection.dataFlow.value.DfaValue; import com.intellij.psi.*; -import com.intellij.psi.util.PropertyUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -126,7 +125,7 @@ public class MethodCallInstruction extends Instruction { private boolean isPureCall() { if (myTargetMethod == null) return false; - return ControlFlowAnalyzer.isPure(myTargetMethod) || PropertyUtil.isSimplePropertyGetter(myTargetMethod); + return ControlFlowAnalyzer.isPure(myTargetMethod); } @Nullable diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java index d588c1cd9957..fa391bc53d6c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java @@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.JavaConstantExpressionEvaluator; import com.intellij.psi.util.PropertyUtil; @@ -41,18 +42,21 @@ public class DfaExpressionFactory { private static Condition parseFalseGetters() { try { - final Pattern pattern = Pattern.compile(Registry.stringValue("ide.dfa.getters.with.side.effects")); - return new Condition() { - @Override - public boolean value(String s) { - return pattern.matcher(s).matches(); - } - }; + String regex = Registry.stringValue("ide.dfa.getters.with.side.effects").trim(); + if (!StringUtil.isEmpty(regex)) { + final Pattern pattern = Pattern.compile(regex); + return new Condition() { + @Override + public boolean value(String s) { + return pattern.matcher(s).matches(); + } + }; + } } catch (Exception e) { LOG.error(e); - return Conditions.alwaysFalse(); } + return Conditions.alwaysFalse(); } private final DfaValueFactory myFactory; @@ -152,10 +156,11 @@ public class DfaExpressionFactory { return (PsiVariable)target; } if (target instanceof PsiMethod) { - if (PropertyUtil.isSimplePropertyGetter((PsiMethod)target)) { - String qName = PsiUtil.getMemberQualifiedName((PsiMethod)target); + PsiMethod method = (PsiMethod)target; + if (PropertyUtil.isSimplePropertyGetter(method) && !(method.getReturnType() instanceof PsiPrimitiveType)) { + String qName = PsiUtil.getMemberQualifiedName(method); if (qName == null || !FALSE_GETTERS.value(qName)) { - return (PsiMethod)target; + return method; } } } diff --git a/java/java-impl/java-impl.iml b/java/java-impl/java-impl.iml index cf601bbd0f76..ee71056b6734 100644 --- a/java/java-impl/java-impl.iml +++ b/java/java-impl/java-impl.iml @@ -56,11 +56,11 @@ + - - + \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/Java18CompletionData.java b/java/java-impl/src/com/intellij/codeInsight/completion/Java18CompletionData.java index 7ab087b78b8f..c169dc8cf0b6 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/Java18CompletionData.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/Java18CompletionData.java @@ -17,9 +17,11 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.TailType; import com.intellij.codeInsight.daemon.impl.analysis.LambdaHighlightingUtil; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.patterns.PsiElementPattern; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.Consumer; import static com.intellij.patterns.PsiJavaPatterns.psiElement; @@ -28,14 +30,14 @@ public class Java18CompletionData extends Java15CompletionData { .afterLeaf(psiElement(JavaTokenType.DOUBLE_COLON)); @Override - public void fillCompletions(final CompletionParameters parameters, final CompletionResultSet result) { + public void fillCompletions(final CompletionParameters parameters, final Consumer result) { PsiElement position = parameters.getPosition(); if (!inComment(position)) { if (AFTER_DOUBLE_COLON.accepts(position)) { PsiMethodReferenceExpression parent = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiMethodReferenceExpression.class); TailType tail = parent != null && !LambdaHighlightingUtil.insertSemicolon(parent.getParent()) ? TailType.SEMICOLON : TailType.NONE; - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), tail)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), tail)); return; } @@ -43,7 +45,7 @@ public class Java18CompletionData extends Java15CompletionData { PsiElement scope = position.getParent(); while (scope != null && !(scope instanceof PsiFile)) { if (scope instanceof PsiClass && ((PsiClass)scope).isInterface()) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.HUMBLE_SPACE_BEFORE_WORD)); break; } scope = scope.getParent(); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaAwareCompletionData.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaAwareCompletionData.java index c49e78287738..c124826a4b58 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaAwareCompletionData.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaAwareCompletionData.java @@ -86,6 +86,4 @@ public class JavaAwareCompletionData extends CompletionData{ } } - public void fillCompletions(CompletionParameters parameters, CompletionResultSet result) { - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index b154c38f9f29..9789ec76a99a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -241,6 +241,8 @@ public class JavaCompletionContributor extends CompletionContributor { addKeywords(parameters, result); + addExpressionVariants(parameters, position, result); + Set usedWords = addReferenceVariants(parameters, result, inheritors); if (psiElement().inside(PsiLiteralExpression.class).accepts(position)) { @@ -264,6 +266,16 @@ public class JavaCompletionContributor extends CompletionContributor { result.stopHere(); } + private void addExpressionVariants(@NotNull CompletionParameters parameters, PsiElement position, CompletionResultSet result) { + if (JavaSmartCompletionContributor.INSIDE_EXPRESSION.accepts(position) && + !JavaCompletionData.AFTER_DOT.accepts(position)) { + JavaCompletionData.addExpectedTypeMembers(parameters, result); + if (SameSignatureCallParametersProvider.IN_CALL_ARGUMENT.accepts(position)) { + new SameSignatureCallParametersProvider().addCompletions(parameters, new ProcessingContext(), result); + } + } + } + public static boolean isInJavaContext(PsiElement position) { return PsiUtilCore.findLanguageFromElement(position).isKindOf(JavaLanguage.INSTANCE); } @@ -382,17 +394,26 @@ public class JavaCompletionContributor extends CompletionContributor { return usedWords; } - private static void addKeywords(CompletionParameters parameters, CompletionResultSet result) { + private static void addKeywords(CompletionParameters parameters, final CompletionResultSet result) { + Consumer noMiddleMatches = new Consumer() { + @Override + public void consume(LookupElement element) { + if (element.getLookupString().startsWith(result.getPrefixMatcher().getPrefix())) { + result.addElement(element); + } + } + }; + PsiElement position = parameters.getPosition(); final Set lookupSet = new LinkedHashSet(); final Set keywordVariants = new HashSet(); final JavaCompletionData completionData = getCompletionData(PsiUtil.getLanguageLevel(position)); completionData.addKeywordVariants(keywordVariants, position, parameters.getOriginalFile()); completionData.completeKeywordsBySet(lookupSet, keywordVariants, position, result.getPrefixMatcher(), parameters.getOriginalFile()); - completionData.fillCompletions(parameters, result); + completionData.fillCompletions(parameters, noMiddleMatches); for (final LookupElement item : lookupSet) { - result.addElement(item); + noMiddleMatches.consume(item); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java index bede9bd170e5..67670d5d2d18 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java @@ -34,6 +34,7 @@ import com.intellij.psi.jsp.JspElementType; import com.intellij.psi.templateLanguages.OuterLanguageElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.util.Consumer; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NonNls; @@ -368,19 +369,19 @@ public class JavaCompletionData extends JavaAwareCompletionData { } } - private static void addStatementKeywords(CompletionResultSet variant, PsiElement position) { - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.SWITCH), TailTypes.SWITCH_LPARENTH)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.WHILE), TailTypes.WHILE_LPARENTH)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.DO), TailTypes.DO_LBRACE)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.FOR), TailTypes.FOR_LPARENTH)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.IF), TailTypes.IF_LPARENTH)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.TRY), TailTypes.TRY_LBRACE)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.THROW), TailType.INSERT_SPACE)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.SYNCHRONIZED), TailTypes.SYNCHRONIZED_LPARENTH)); + private static void addStatementKeywords(Consumer variant, PsiElement position) { + variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.SWITCH), TailTypes.SWITCH_LPARENTH)); + variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.WHILE), TailTypes.WHILE_LPARENTH)); + variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DO), TailTypes.DO_LBRACE)); + variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.FOR), TailTypes.FOR_LPARENTH)); + variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.IF), TailTypes.IF_LPARENTH)); + variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.TRY), TailTypes.TRY_LBRACE)); + variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.THROW), TailType.INSERT_SPACE)); + variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); + variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.SYNCHRONIZED), TailTypes.SYNCHRONIZED_LPARENTH)); if (PsiUtil.getLanguageLevel(position).isAtLeast(LanguageLevel.JDK_1_4)) { - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.ASSERT), TailType.INSERT_SPACE)); + variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.ASSERT), TailType.INSERT_SPACE)); } TailType returnTail = getReturnTail(position); @@ -388,11 +389,10 @@ public class JavaCompletionData extends JavaAwareCompletionData { if (returnTail != TailType.NONE) { ret = new OverrideableSpace(ret, returnTail); } - variant.addElement(ret); + variant.consume(ret); } - @Override - public void fillCompletions(CompletionParameters parameters, final CompletionResultSet result) { + public void fillCompletions(CompletionParameters parameters, final Consumer result) { final PsiElement position = parameters.getPosition(); if (PsiTreeUtil.getParentOfType(position, PsiComment.class, false) != null) { return; @@ -407,15 +407,15 @@ public class JavaCompletionData extends JavaAwareCompletionData { if (!psiElement().withSuperParent(2, PsiSwitchStatement.class).afterLeaf("{").accepts(statement)) { PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(prevLeaf, PsiTryStatement.class); if (tryStatement == null || tryStatement.getCatchSections().length > 0 || tryStatement.getFinallyBlock() != null) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } } if (isStatementPosition(position)) { if (PsiTreeUtil.getParentOfType(position, PsiSwitchStatement.class, false, PsiMember.class) != null) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.CASE), TailType.INSERT_SPACE)); - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.CASE_COLON)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.CASE), TailType.INSERT_SPACE)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.CASE_COLON)); if (START_SWITCH.accepts(position)) { return; } @@ -431,7 +431,7 @@ public class JavaCompletionData extends JavaAwareCompletionData { final boolean insideInheritorClass = PsiUtil.isLanguageLevel8OrHigher(position) && isInsideInheritorClass(position); if (!afterDot || insideQualifierClass || insideInheritorClass) { if (!afterDot || insideQualifierClass) { - result.addElement(createKeyword(position, PsiKeyword.THIS)); + result.consume(createKeyword(position, PsiKeyword.THIS)); } final LookupItem superItem = (LookupItem)createKeyword(position, PsiKeyword.SUPER); @@ -453,18 +453,18 @@ public class JavaCompletionData extends JavaAwareCompletionData { }); } - result.addElement(superItem); + result.consume(superItem); } } if (isExpressionPosition(position)) { if (PsiTreeUtil.getParentOfType(position, PsiAnnotation.class) == null) { - result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); - result.addElement(createKeyword(position, PsiKeyword.NULL)); + result.consume(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); + result.consume(createKeyword(position, PsiKeyword.NULL)); } if (mayExpectBoolean(parameters)) { - result.addElement(createKeyword(position, PsiKeyword.TRUE)); - result.addElement(createKeyword(position, PsiKeyword.FALSE)); + result.consume(createKeyword(position, PsiKeyword.TRUE)); + result.consume(createKeyword(position, PsiKeyword.FALSE)); } } @@ -473,22 +473,22 @@ public class JavaCompletionData extends JavaAwareCompletionData { !(file instanceof PsiJavaCodeReferenceCodeFragment) && !(file instanceof PsiTypeCodeFragment)) { if (prevLeaf == null) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.PACKAGE), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.PACKAGE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); } else if (END_OF_BLOCK.isAcceptable(position, position) && PsiTreeUtil.getParentOfType(position, PsiMember.class) == null) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } if ((isInsideParameterList(position) || isAtResourceVariableStart(position) || isAtCatchVariableStart(position)) && !psiElement().afterLeaf(PsiKeyword.FINAL).accepts(position) && !AFTER_DOT.accepts(position)) { - result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); } if (isInstanceofPlace(position)) { - result.addElement(LookupElementDecorator.withInsertHandler( + result.consume(LookupElementDecorator.withInsertHandler( createKeyword(position, PsiKeyword.INSTANCEOF), new InsertHandler>() { @Override @@ -497,7 +497,7 @@ public class JavaCompletionData extends JavaAwareCompletionData { if (tailType.isApplicable(context)) { tailType.processTail(context.getEditor(), context.getTailOffset()); } - + if ('!' == context.getCompletionChar()) { context.setAddCompletionChar(false); context.commitDocument(); @@ -515,13 +515,13 @@ public class JavaCompletionData extends JavaAwareCompletionData { if (isSuitableForClass(position)) { for (String s : ModifierChooser.getKeywords(position)) { - result.addElement(new OverrideableSpace(createKeyword(position, s), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, s), TailType.HUMBLE_SPACE_BEFORE_WORD)); } - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.CLASS), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.CLASS), TailType.HUMBLE_SPACE_BEFORE_WORD)); if (PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, true, PsiMember.class) == null) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.INTERFACE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.INTERFACE), TailType.HUMBLE_SPACE_BEFORE_WORD)); if (PsiUtil.getLanguageLevel(position).isAtLeast(LanguageLevel.JDK_1_5)) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.ENUM), TailType.INSERT_SPACE)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.ENUM), TailType.INSERT_SPACE)); } } } @@ -529,22 +529,14 @@ public class JavaCompletionData extends JavaAwareCompletionData { addPrimitiveTypes(result, position); if (isAfterTypeDot(position)) { - result.addElement(createKeyword(position, PsiKeyword.CLASS)); + result.consume(createKeyword(position, PsiKeyword.CLASS)); } addUnfinishedMethodTypeParameters(position, result); - if (JavaSmartCompletionContributor.INSIDE_EXPRESSION.accepts(position) && - !AFTER_DOT.accepts(position)) { - addExpectedTypeMembers(parameters, result); - if (SameSignatureCallParametersProvider.IN_CALL_ARGUMENT.accepts(position)) { - new SameSignatureCallParametersProvider().addCompletions(parameters, new ProcessingContext(), result); - } - } - if (JavaMemberNameCompletionContributor.INSIDE_TYPE_PARAMS_PATTERN.accepts(position)) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.EXTENDS), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.SUPER), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.EXTENDS), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.SUPER), TailType.HUMBLE_SPACE_BEFORE_WORD)); } } @@ -608,7 +600,7 @@ public class JavaCompletionData extends JavaAwareCompletionData { } } - private static void addUnfinishedMethodTypeParameters(PsiElement position, CompletionResultSet result) { + private static void addUnfinishedMethodTypeParameters(PsiElement position, final Consumer result) { final ProcessingContext context = new ProcessingContext(); if (psiElement().inside( psiElement(PsiTypeElement.class).afterLeaf( @@ -621,7 +613,7 @@ public class JavaCompletionData extends JavaAwareCompletionData { } if (current instanceof PsiClass) { for (PsiTypeParameter typeParameter : list.getTypeParameters()) { - result.addElement(new JavaPsiClassReferenceElement(typeParameter)); + result.consume(new JavaPsiClassReferenceElement(typeParameter)); } } } @@ -643,7 +635,7 @@ public class JavaCompletionData extends JavaAwareCompletionData { isAfterPrimitiveOrArrayType(position); } - private static void addPrimitiveTypes(CompletionResultSet result, PsiElement position) { + private static void addPrimitiveTypes(final Consumer result, PsiElement position) { if (AFTER_DOT.accepts(position)) { return; } @@ -653,9 +645,9 @@ public class JavaCompletionData extends JavaAwareCompletionData { if (afterNew) { PsiElementFactory factory = JavaPsiFacade.getElementFactory(position.getProject()); for (String primitiveType : PRIMITIVE_TYPES) { - result.addElement(PsiTypeLookupItem.createLookupItem(factory.createTypeFromText(primitiveType + "[]", null), null)); + result.consume(PsiTypeLookupItem.createLookupItem(factory.createTypeFromText(primitiveType + "[]", null), null)); } - result.addElement(PsiTypeLookupItem.createLookupItem(factory.createTypeFromText("void[]", null), null)); + result.consume(PsiTypeLookupItem.createLookupItem(factory.createTypeFromText("void[]", null), null)); return; } @@ -677,14 +669,14 @@ public class JavaCompletionData extends JavaAwareCompletionData { expressionPosition || isStatementPosition(position)) { for (String primitiveType : PRIMITIVE_TYPES) { - result.addElement(createKeyword(position, primitiveType)); + result.consume(createKeyword(position, primitiveType)); } } if (declaration) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.VOID), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.VOID), TailType.HUMBLE_SPACE_BEFORE_WORD)); } else if (typeFragment && ((PsiTypeCodeFragment)position.getContainingFile()).isVoidValid()) { - result.addElement(createKeyword(position, PsiKeyword.VOID)); + result.consume(createKeyword(position, PsiKeyword.VOID)); } } @@ -696,7 +688,7 @@ public class JavaCompletionData extends JavaAwareCompletionData { return psiElement().insideStarting(psiElement(PsiTypeElement.class).withParent(PsiCatchSection.class)).accepts(position); } - private static void addBreakContinue(CompletionResultSet result, PsiElement position) { + private static void addBreakContinue(Consumer result, PsiElement position) { PsiLoopStatement loop = PsiTreeUtil.getParentOfType(position, PsiLoopStatement.class); LookupElement br = createKeyword(position, PsiKeyword.BREAK); @@ -714,11 +706,11 @@ public class JavaCompletionData extends JavaAwareCompletionData { cont = TailTypeDecorator.withTail(cont, tailType); if (loop != null && new InsideElementFilter(new ClassFilter(PsiStatement.class)).isAcceptable(position, loop)) { - result.addElement(br); - result.addElement(cont); + result.consume(br); + result.consume(cont); } if (psiElement().inside(PsiSwitchStatement.class).accepts(position)) { - result.addElement(br); + result.consume(br); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java index d526f1bd828b..b416e851610e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java @@ -50,8 +50,16 @@ public class JavaNoVariantsDelegator extends CompletionContributor { result.restartCompletionWhenNothingMatches(); } + InheritorsHolder holder = new InheritorsHolder(parameters.getPosition(), result); + for (CompletionResult plainResult : plainResults) { + Object o = plainResult.getLookupElement().getObject(); + if (o instanceof PsiClass) { + holder.registerClass((PsiClass)o); + } + } + if (empty) { - delegate(parameters, JavaCompletionSorting.addJavaSorting(parameters, result)); + delegate(parameters, JavaCompletionSorting.addJavaSorting(parameters, result), holder); } else if (Registry.is("ide.completion.show.better.matching.classes")) { if (parameters.getCompletionType() == CompletionType.BASIC && parameters.getInvocationCount() <= 1 && @@ -59,7 +67,6 @@ public class JavaNoVariantsDelegator extends CompletionContributor { JavaCompletionContributor.isClassNamePossible(parameters) && !JavaSmartCompletionContributor.AFTER_NEW.accepts(parameters.getPosition())) { result = result.withPrefixMatcher(new BetterPrefixMatcher(result.getPrefixMatcher(), BetterPrefixMatcher.getBestMatchingDegree(plainResults))); - InheritorsHolder holder = new InheritorsHolder(parameters.getPosition(), result); for (CompletionResult plainResult : plainResults) { LookupElement element = plainResult.getLookupElement(); if (element instanceof TypeArgumentCompletionProvider.TypeArgsLookupElement) { @@ -86,7 +93,7 @@ public class JavaNoVariantsDelegator extends CompletionContributor { return true; } - private static void delegate(CompletionParameters parameters, final CompletionResultSet result) { + private static void delegate(CompletionParameters parameters, final CompletionResultSet result, final InheritorsHolder inheritorsHolder) { if (parameters.getCompletionType() == CompletionType.BASIC) { PsiElement position = parameters.getPosition(); suggestCollectionUtilities(parameters, result, position); @@ -94,7 +101,7 @@ public class JavaNoVariantsDelegator extends CompletionContributor { if (parameters.getInvocationCount() <= 1 && (JavaCompletionContributor.mayStartClassName(result) || suggestMetaAnnotations(parameters)) && JavaCompletionContributor.isClassNamePossible(parameters)) { - suggestNonImportedClasses(parameters, result, null); + suggestNonImportedClasses(parameters, result, inheritorsHolder); return; } diff --git a/java/java-impl/src/com/intellij/codeInspection/SurroundWithIfFix.java b/java/java-impl/src/com/intellij/codeInspection/SurroundWithIfFix.java index 3bdbc7c067fd..9afbb9de848b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/SurroundWithIfFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/SurroundWithIfFix.java @@ -27,6 +27,7 @@ import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtilBase; import com.intellij.util.IncorrectOperationException; +import com.siyeh.ipp.trivialif.MergeIfAndIntention; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -57,7 +58,7 @@ public class SurroundWithIfFix implements LocalQuickFix { PsiFile file = element.getContainingFile(); PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); Document document = documentManager.getDocument(file); - if (!FileModificationService.getInstance().prepareFileForWrite(file)) return; + if (document == null || !FileModificationService.getInstance().prepareFileForWrite(file)) return; PsiElement[] elements = {anchorStatement}; PsiElement prev = PsiTreeUtil.skipSiblingsBackward(anchorStatement, PsiWhiteSpace.class); if (prev instanceof PsiComment && JavaSuppressionUtil.getSuppressedInspectionIdsIn(prev) != null) { @@ -71,6 +72,11 @@ public class SurroundWithIfFix implements LocalQuickFix { document.replaceString(textRange.getStartOffset(), textRange.getEndOffset(),newText); editor.getCaretModel().moveToOffset(textRange.getEndOffset() + newText.length()); + + PsiDocumentManager.getInstance(project).commitAllDocuments(); + + new MergeIfAndIntention().invoke(project, editor, file); + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } catch (IncorrectOperationException e) { diff --git a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java index cb215d06380d..0a61a7c94c06 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -50,11 +50,14 @@ import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiFormatUtilBase; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.util.SmartList; +import com.intellij.util.Url; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.builtInWebServer.BuiltInWebBrowserUrlProvider; import java.util.LinkedHashSet; import java.util.List; @@ -684,7 +687,7 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext } @Nullable - public static List findUrlForClass(PsiClass aClass) { + public static List findUrlForClass(@NotNull PsiClass aClass) { String qName = aClass.getQualifiedName(); if (qName == null) return null; @@ -696,18 +699,18 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext String packageName = ((PsiJavaFile)file).getPackageName(); String relPath; - if (packageName.length() > 0) { - relPath = packageName.replace('.', '/') + '/' + qName.substring(packageName.length() + 1) + HTML_EXTENSION; + if (packageName.isEmpty()) { + relPath = qName + HTML_EXTENSION; } else { - relPath = qName + HTML_EXTENSION; + relPath = packageName.replace('.', '/') + '/' + qName.substring(packageName.length() + 1) + HTML_EXTENSION; } return findUrlForVirtualFile(file.getProject(), virtualFile, relPath); } @Nullable - public static List findUrlForVirtualFile(final Project project, final VirtualFile virtualFile, final String relPath) { + public static List findUrlForVirtualFile(@NotNull Project project, @NotNull VirtualFile virtualFile, @NotNull String relPath) { final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); Module module = fileIndex.getModuleForFile(virtualFile); if (module == null) { @@ -728,11 +731,25 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext } } - final List orderEntries = fileIndex.getOrderEntriesForFile(virtualFile); - for (OrderEntry orderEntry : orderEntries) { - final String[] files = JavadocOrderRootType.getUrls(orderEntry); - final List httpRoot = PlatformDocumentationUtil.getHttpRoots(files, relPath); - if (httpRoot != null) return httpRoot; + for (OrderEntry orderEntry : fileIndex.getOrderEntriesForFile(virtualFile)) { + for (VirtualFile root : orderEntry.getFiles(JavadocOrderRootType.getInstance())) { + if (root.getFileSystem() == JarFileSystem.getInstance()) { + VirtualFile file = root.findFileByRelativePath(relPath); + List urls = file == null ? null : BuiltInWebBrowserUrlProvider.getUrls(file, project, null); + if (!ContainerUtil.isEmpty(urls)) { + List result = new SmartList(); + for (Url url : urls) { + result.add(url.toExternalForm()); + } + return result; + } + } + } + + List httpRoot = PlatformDocumentationUtil.getHttpRoots(JavadocOrderRootType.getUrls(orderEntry), relPath); + if (httpRoot != null) { + return httpRoot; + } } return null; } diff --git a/java/java-impl/src/com/intellij/openapi/projectRoots/JavaLanguageTestDataChecker.java b/java/java-impl/src/com/intellij/openapi/projectRoots/JavaLanguageTestDataChecker.java new file mode 100644 index 000000000000..f3eafb51fbea --- /dev/null +++ b/java/java-impl/src/com/intellij/openapi/projectRoots/JavaLanguageTestDataChecker.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2015 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.openapi.projectRoots; + +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; + +public class JavaLanguageTestDataChecker implements LanguageTestDataChecker { + + @Override + @NotNull + public FileType getFileType() { + return StdFileTypes.JAVA; + } + + @Override + public boolean isTestData(@NotNull Project project, @NotNull VirtualFile virtualFile) { + ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex(); + return !index.isUnderSourceRootOfType(virtualFile, JavaModuleSourceRootTypes.SOURCES); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/DuplicateInnerClass.java b/java/java-tests/testData/codeInsight/completion/normal/DuplicateInnerClass.java new file mode 100644 index 000000000000..86031e5e9724 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/DuplicateInnerClass.java @@ -0,0 +1,8 @@ +class Foo { + + public static void main(String[] args) { + Innex + } + + private static final class Inner { } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/afterIfCondition.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/afterIfCondition.java new file mode 100644 index 000000000000..68c92a641531 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/afterIfCondition.java @@ -0,0 +1,11 @@ +// "Surround with 'if (i != null)'" "true" +import org.jetbrains.annotations.Nullable; + +class A { + void foo(@Nullable String i) { + if (i != null && i.length() > 0) { + if (i != "a") { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/beforeIfCondition.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/beforeIfCondition.java new file mode 100644 index 000000000000..ac6851a6c3d7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithIf/beforeIfCondition.java @@ -0,0 +1,11 @@ +// "Surround with 'if (i != null)'" "true" +import org.jetbrains.annotations.Nullable; + +class A { + void foo(@Nullable String i) { + if (i.length() > 0) { + if (i != "a") { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/FalseGetters.java b/java/java-tests/testData/inspection/dataFlow/fixture/FalseGetters.java new file mode 100644 index 000000000000..162aedeac284 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/FalseGetters.java @@ -0,0 +1,21 @@ +class A { + private String s; + private int next = 0; + + public A(final String s) { + this.s = s; + } + + private char getChar() { + return s.charAt(next++); + } + + private void foo() { + char c = getChar(); + if (c == 'a') { + if (getChar() == 'b') { + System.out.println("ab"); + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy index 157e7a341be3..3781cfffcf88 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -1599,7 +1599,7 @@ class Foo { myFixture.configureByText "a.java", "class Foo {{ }}" myFixture.type('a') joinAutopopup() - myFixture.type('\na') + myFixture.type('\nf') joinCompletion() assert lookup } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index b95761cefba3..116bb969dc74 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -389,7 +389,7 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase { checkResult() } - public void testFieldType() throws Throwable { doTest('\n'); } + public void testFieldType() { doTest(); } public void testPackageInAnnoParam() throws Throwable { doTest(); @@ -903,7 +903,7 @@ public class ListUtils { checkResult(); } - public void testSecondAnonymousClassParameter() throws Throwable { doTest('\n'); } + public void testSecondAnonymousClassParameter() { doTest(); } public void testSpaceAfterReturn() throws Throwable { configure() @@ -1269,6 +1269,12 @@ class XInternalError {} assert LookupElementPresentation.renderElement(items[0]).tailText == ' (java.lang)' } + public void testDuplicateInnerClass() { + configure() + def items = myFixture.lookupElements.findAll { it.lookupString == 'Inner' } + assert items.size() == 1 + } + public void testSameSignature() { configure() myFixture.assertPreferredCompletionItems(0, 's', 's, file', 's, file, a') diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/VariablesCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/VariablesCompletionTest.groovy index 4bf387841eae..afd99d40dee4 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/VariablesCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/VariablesCompletionTest.groovy @@ -45,7 +45,7 @@ public class VariablesCompletionTest extends LightFixtureCompletionTestCase { } public void testLocals3() throws Exception { - doSelectTest("TestSource3.java", "TestResult3.java"); + doTest("TestSource3.java", "TestResult3.java"); } public void testLocals4() throws Exception { @@ -53,7 +53,7 @@ public class VariablesCompletionTest extends LightFixtureCompletionTestCase { } public void testLocals5() throws Exception { - doSelectTest("TestSource5.java", "TestResult5.java"); + doTest("TestSource5.java", "TestResult5.java"); } public void testLocals6() throws Exception { diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index 2a105e5bf7d0..5805808d3a17 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -271,6 +271,7 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { public void testUnusedCallDoesNotMakeUnknown() { doTest(); } public void testEmptyCallDoesNotMakeNullable() { doTest(); } public void testGettersAndPureNoFlushing() { doTest(); } + public void testFalseGetters() { doTest(); } public void testNotNullAfterDereference() { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java index c7664bd15bc8..adb0d9f35c50 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.completion.SmartTypeCompletionDfaTest; import com.intellij.codeInsight.daemon.quickFix.AddAssertStatementFixTest; import com.intellij.codeInsight.daemon.quickFix.ReplaceFromOfNullableFixTest; import com.intellij.codeInsight.daemon.quickFix.ReplaceWithOfNullableFixTest; +import com.intellij.codeInsight.daemon.quickFix.SurroundWithIfFixTest; import com.intellij.slicer.SliceBackwardTest; import com.intellij.slicer.SliceTreeTest; import junit.framework.Test; @@ -49,6 +50,7 @@ public class DataFlowInspectionTestSuite { suite.addTestSuite(NullableStuffInspection14Test.class); suite.addTestSuite(AddAssertStatementFixTest.class); + suite.addTestSuite(SurroundWithIfFixTest.class); suite.addTestSuite(ReplaceWithOfNullableFixTest.class); suite.addTestSuite(ReplaceFromOfNullableFixTest.class); return suite; diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java index fa09f74d71ea..70ed18f5f98b 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java @@ -33,6 +33,8 @@ import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.PairFunction; import com.intellij.util.PlatformUtils; @@ -207,7 +209,8 @@ final class DefaultWebServerRootsProvider extends WebServerRootsProvider { DirectoryInfo info = directoryIndex.getInfoForFile(file); // we serve excluded files if (!info.isExcluded() && !info.isInProject()) { - return null; + // javadoc jars is "not under project", but actually is, so, let's check project library table + return file.getFileSystem() == JarFileSystem.getInstance() ? getInfoForDocJar(file, project) : null; } VirtualFile root = info.getSourceRoot(); @@ -245,6 +248,59 @@ final class DefaultWebServerRootsProvider extends WebServerRootsProvider { } } + @Nullable + private static PathInfo getInfoForDocJar(@NotNull final VirtualFile file, @NotNull Project project) { + final OrderRootType javaDocRootType = JavadocOrderRootType.getInstance(); + if (javaDocRootType == null) { + return null; + } + + class LibraryProcessor implements Processor { + PathInfo result; + String moduleName; + + @Override + public boolean process(Library library) { + for (VirtualFile root : library.getFiles(javaDocRootType)) { + if (VfsUtilCore.isAncestor(root, file, false)) { + result = new PathInfo(file, root, moduleName, true); + return false; + } + } + return true; + } + } + + LibraryProcessor processor = new LibraryProcessor(); + AccessToken token = ReadAction.start(); + try { + ModuleManager moduleManager = ModuleManager.getInstance(project); + for (Module module : moduleManager.getModules()) { + if (module.isDisposed()) { + continue; + } + + processor.moduleName = module.getName(); + ModuleRootManager.getInstance(module).orderEntries().forEachLibrary(processor); + if (processor.result != null) { + return processor.result; + } + } + + processor.moduleName = null; + for (Library library : LibraryTablesRegistrar.getInstance().getLibraryTable(project).getLibraries()) { + if (!processor.process(library)) { + return processor.result; + } + } + } + finally { + token.finish(); + } + + return null; + } + @Nullable private static String getModuleNameQualifier(@NotNull Project project, @Nullable Module module) { if (module != null && diff --git a/platform/core-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java b/platform/core-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java index 966d8173c150..fa519f0249bb 100644 --- a/platform/core-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java +++ b/platform/core-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -19,10 +19,11 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.ex.http.HttpFileSystem; +import com.intellij.util.SmartList; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; @@ -30,7 +31,6 @@ import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; public class PlatformDocumentationUtil { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.documentation.PlatformDocumentationUtil"); private static final @NonNls Pattern ourLtFixupPattern = Pattern.compile("<([^/^\\w^!])"); @@ -38,19 +38,23 @@ public class PlatformDocumentationUtil { private static final @NonNls String LT_ENTITY = "<"; @Nullable - public static List getHttpRoots(final String[] roots, String relPath) { - final ArrayList result = new ArrayList(); + public static List getHttpRoots(@NotNull String[] roots, String relPath) { + List result = new SmartList(); for (String root : roots) { - final VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(root); + VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(root); if (virtualFile != null) { if (virtualFile.getFileSystem() instanceof HttpFileSystem) { String url = virtualFile.getUrl(); - if (!url.endsWith("/")) url += "/"; + if (!url.endsWith("/")) { + url += "/"; + } result.add(url + relPath); } else { VirtualFile file = virtualFile.findFileByRelativePath(relPath); - if (file != null) result.add(file.getUrl()); + if (file != null) { + result.add(file.getUrl()); + } } } } diff --git a/platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java b/platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java index f624957695d6..52bb0a6d3409 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java @@ -23,6 +23,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.EmptyRunnable; +import com.intellij.openapi.util.Ref; import com.intellij.util.Function; import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.NotNull; @@ -139,4 +140,26 @@ public class BackgroundTaskUtil { result.run(); indicator.stop(); } + + @CalledInAwt + @Nullable + public static T tryComputeFast(@NotNull final Function backgroundTask, + final int waitMillis) { + final Ref resultRef = new Ref(); + ProgressIndicator indicator = executeAndTryWait(new Function() { + @Override + public Runnable fun(final ProgressIndicator indicator) { + final T result = backgroundTask.fun(indicator); + return new Runnable() { + @Override + public void run() { + resultRef.set(result); + } + }; + } + }, null, waitMillis, false); + indicator.cancel(); + + return resultRef.get(); + } } diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java index cf487b335e6a..d634a57b7612 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java @@ -656,6 +656,12 @@ public class DiffUtil { return document.getImmutableCharSequence().subSequence(otherRange.getStartOffset(), otherRange.getEndOffset()); } + /** + * Return affected range, without non-internal newlines + *

+ * we consider '\n' not as a part of line, but a separator between lines + * ex: if last line is not empty, the last symbol will not be '\n' + */ @NotNull public static TextRange getLinesRange(@NotNull Document document, int line1, int line2) { if (line1 == line2) { diff --git a/platform/lang-api/src/com/intellij/openapi/projectRoots/LanguageTestDataChecker.java b/platform/lang-api/src/com/intellij/openapi/projectRoots/LanguageTestDataChecker.java new file mode 100644 index 000000000000..bca5e35211ee --- /dev/null +++ b/platform/lang-api/src/com/intellij/openapi/projectRoots/LanguageTestDataChecker.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2015 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.openapi.projectRoots; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +/** + * Files for which #isTestData returns true won't be processed by + * Optimize Imports, Reformat Code and Rearrange Code actions during before-commit stage. + * + * When developing language plugin it is improperly to perform any modification actions + * for language test data files (files that looks like source code, but are used only as test data) + * on commit, even if appropriate checkboxes in commit dialog are turned on. + */ +public interface LanguageTestDataChecker { + + ExtensionPointName EP_NAME = + new ExtensionPointName("com.intellij.languageTestDataChecker"); + + @NotNull + FileType getFileType(); + + boolean isTestData(@NotNull Project project, @NotNull VirtualFile virtualFile); + +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java index d687848a4949..36e8f4005070 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java @@ -705,33 +705,35 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr @Override public void actionPerformed(AnActionEvent e) { - if (myElement != null) { - final PsiElement element = myElement.getElement(); - final DocumentationProvider provider = DocumentationManager.getProviderFromElement(element); - final PsiElement originalElement = DocumentationManager.getOriginalElement(element); - boolean processed = false; - if (provider instanceof CompositeDocumentationProvider) { - for (DocumentationProvider p : ((CompositeDocumentationProvider)provider).getAllProviders()) { - if (p instanceof ExternalDocumentationHandler && ((ExternalDocumentationHandler)p).handleExternal(element, originalElement)) { - processed = true; - break; - } - } - } + if (myElement == null) { + return; + } - if (!processed) { - final Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(e.getDataContext()); - final List urls; - if (!StringUtil.isEmptyOrSpaces(myEffectiveExternalUrl)) { - urls = Collections.singletonList(myEffectiveExternalUrl); - } else { - urls = provider.getUrlFor(element, originalElement); - assert urls != null : provider; - assert !urls.isEmpty() : provider; + final PsiElement element = myElement.getElement(); + final DocumentationProvider provider = DocumentationManager.getProviderFromElement(element); + final PsiElement originalElement = DocumentationManager.getOriginalElement(element); + boolean processed = false; + if (provider instanceof CompositeDocumentationProvider) { + for (DocumentationProvider p : ((CompositeDocumentationProvider)provider).getAllProviders()) { + if (p instanceof ExternalDocumentationHandler && ((ExternalDocumentationHandler)p).handleExternal(element, originalElement)) { + processed = true; + break; } - ExternalJavaDocAction.showExternalJavadoc(urls, component); } } + + if (!processed) { + List urls; + if (!StringUtil.isEmptyOrSpaces(myEffectiveExternalUrl)) { + urls = Collections.singletonList(myEffectiveExternalUrl); + } + else { + urls = provider.getUrlFor(element, originalElement); + assert urls != null : provider; + assert !urls.isEmpty() : provider; + } + ExternalJavaDocAction.showExternalJavadoc(urls, PlatformDataKeys.CONTEXT_COMPONENT.getData(e.getDataContext())); + } } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java b/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java index 9d73cd569c40..f974d35afd8d 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -35,11 +35,13 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.util.ArrayUtil; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; -import java.util.HashSet; import java.util.List; +import java.util.Set; public class ExternalJavaDocAction extends AnAction { @@ -90,8 +92,8 @@ public class ExternalJavaDocAction extends AnAction { } } - public static void showExternalJavadoc(List urls, Component component) { - final HashSet set = new HashSet(urls); + public static void showExternalJavadoc(@NotNull List urls, Component component) { + Set set = new THashSet(urls); if (set.size() > 1) { JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep("Choose external documentation root", ArrayUtil.toStringArray(set)) { @Override diff --git a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureDialogBase.java b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureDialogBase.java index 0772fe6e487c..14e68d6c1069 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureDialogBase.java +++ b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureDialogBase.java @@ -48,7 +48,6 @@ import com.intellij.ui.treeStructure.Tree; import com.intellij.util.Alarm; import com.intellij.util.Consumer; import com.intellij.util.IJSwingUtilities; -import com.intellij.util.PlatformIcons; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.table.JBListTable; import com.intellij.util.ui.table.JBTableRowEditor; @@ -689,7 +688,7 @@ public abstract class ChangeSignatureDialogBase extends JBSplitter implements Data }); } - public static void mergeListItems(@NotNull CollectionListModel listModel, @NotNull List newItems) { + static void mergeListItems(@NotNull CollectionListModel listModel, @NotNull List newItems) { + boolean isEmptyList = listModel.getSize() == 0; + if (isEmptyList) { + listModel.add(newItems); + return; + } + // remove items for (int i = listModel.getSize() - 1; i >= 0; i--) { if (!newItems.contains(listModel.getElementAt(i))) { @@ -574,7 +580,8 @@ public abstract class FinderRecursivePanel extends JBSplitter implements Data final FinderRecursivePanel childPanel = (FinderRecursivePanel)myChild; if (withUpdatePanel) { childPanel.init(); - } else { + } + else { childPanel.initWithoutUpdatePanel(); } } diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index faea9a0a61ee..2aa532f8303d 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -254,6 +254,7 @@ + diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 099088b2cb62..cc38adeb76fc 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -294,7 +294,7 @@ ide.structural.navigation.visit.fields.description=Whether fields should be stop ide.non.english.keyboard.layout.fix=false ide.non.english.keyboard.layout.fix.description=Enables a fix for key codes with non-English keyboard layouts -ide.dfa.getters.with.side.effects=java\\.nio\\..*ByteBuffer\\.get.* +ide.dfa.getters.with.side.effects= ide.dfa.getters.with.side.effects.description=A regex on qualified names of methods that look like getters but are not. For Constant Conditions & Exception inspection ide.dfa.time.limit.online=1000 @@ -368,6 +368,7 @@ show.flex.debug.design.view=false diff.patience.alg=false diff.status.tracker.skip.spaces=true +diff.status.tracker.byword.delay=200 diff.verify.iterable=false diff.unimportant.line.char.count=3 diff.unimportant.line.char.count.description=Threshold number of non-space characters to mark line as unimportant diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/checkin/CheckinHandlerUtil.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/checkin/CheckinHandlerUtil.java index 4a5a644d4fbe..0a7389951b29 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/checkin/CheckinHandlerUtil.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/checkin/CheckinHandlerUtil.java @@ -16,10 +16,10 @@ package com.intellij.openapi.vcs.checkin; import com.intellij.openapi.components.StorageScheme; -import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ex.ProjectEx; +import com.intellij.openapi.projectRoots.LanguageTestDataChecker; import com.intellij.openapi.roots.GeneratedSourcesFilter; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; @@ -30,7 +30,6 @@ import com.intellij.psi.PsiManager; import com.intellij.psi.util.PsiUtilCore; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import javax.swing.*; import java.util.ArrayList; @@ -67,7 +66,8 @@ public class CheckinHandlerUtil { for (VirtualFile file : selectedFiles) { if (file.isValid()) { - if (isUnderProjectFileDir(projectFileDir, file) || !isFileUnderSourceRoot(project, file)) { + if (isUnderProjectFileDir(projectFileDir, file) || !isFileUnderSourceRoot(project, file) + || isLanguageTestData(project, file)) { continue; } PsiFile psiFile = psiManager.findFile(file); @@ -83,12 +83,17 @@ public class CheckinHandlerUtil { private static boolean isFileUnderSourceRoot(@NotNull Project project, @NotNull VirtualFile file) { ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex(); - if (StdFileTypes.JAVA == file.getFileType()) { - return index.isUnderSourceRootOfType(file, JavaModuleSourceRootTypes.SOURCES) && !index.isInLibrarySource(file); - } - else { - return index.isInContent(file) && !index.isInLibrarySource(file) ; + return index.isInContent(file) && !index.isInLibrarySource(file); + } + + private static boolean isLanguageTestData(@NotNull Project project, @NotNull VirtualFile file) { + for (LanguageTestDataChecker checker : LanguageTestDataChecker.EP_NAME.getExtensions()) { + if (checker.getFileType() == file.getFileType() + && checker.isTestData(project, file)) { + return true; + } } + return false; } static void disableWhenDumb(@NotNull Project project, @NotNull JCheckBox checkBox, @NotNull String tooltip) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java index ad342908c47f..bc5de9fbda92 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vcs.ex; +import com.intellij.diff.util.DiffUtil; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.undo.UndoConstants; @@ -41,14 +42,15 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.EditorNotificationPanel; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.diff.FilesTooBigForDiffException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.*; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.List; +import static com.intellij.diff.util.DiffUtil.getLineCount; + /** * @author irengrig * author: lesya @@ -60,7 +62,6 @@ public class LineStatusTracker { private static final Key PANEL_KEY = new Key("LineStatusTracker.CanNotCalculateDiffPanel"); - private final Object myLock = new Object(); private boolean myInitialized; @NotNull private final Project myProject; @@ -103,51 +104,50 @@ public class LineStatusTracker { myRanges = new ArrayList(); } + @CalledInAwt public void initialize(@NotNull final String vcsContent, @NotNull RevisionPack baseRevisionNumber) { myApplication.assertIsDispatchThread(); + if (myReleased) return; - synchronized (myLock) { - try { - if (myReleased) return; - if (myBaseRevisionNumber != null && myBaseRevisionNumber.contains(baseRevisionNumber)) return; + try { + if (myBaseRevisionNumber != null && myBaseRevisionNumber.contains(baseRevisionNumber)) return; - myBaseRevisionNumber = baseRevisionNumber; + myBaseRevisionNumber = baseRevisionNumber; - myVcsDocument.setReadOnly(false); - myVcsDocument.setText(vcsContent); - myVcsDocument.setReadOnly(true); - reinstallRanges(); + myVcsDocument.setReadOnly(false); + myVcsDocument.setText(vcsContent); + myVcsDocument.setReadOnly(true); + reinstallRanges(); - if (myDocumentListener == null) { - myDocumentListener = new MyDocumentListener(); - myDocument.addDocumentListener(myDocumentListener); - } - } - finally { - myInitialized = true; + if (myDocumentListener == null) { + myDocumentListener = new MyDocumentListener(); + myDocument.addDocumentListener(myDocumentListener); } } + finally { + myInitialized = true; + } } + @CalledInAwt private void reinstallRanges() { myApplication.assertIsDispatchThread(); - synchronized (myLock) { - removeAnathema(); - removeHighlightersFromMarkupModel(); - try { - myRanges = new RangesBuilder(myDocument, myVcsDocument, myMode).getRanges(); - } - catch (FilesTooBigForDiffException e) { - installAnathema(); - return; - } - for (final Range range : myRanges) { - range.setHighlighter(createHighlighter(range)); - } + removeAnathema(); + removeHighlightersFromMarkupModel(); + try { + myRanges = new RangesBuilder(myDocument, myVcsDocument, myMode).getRanges(); + } + catch (FilesTooBigForDiffException e) { + installAnathema(); + return; + } + for (final Range range : myRanges) { + range.setHighlighter(createHighlighter(range)); } } + @CalledInAwt private void installAnathema() { myAnathemaThrown = true; final FileEditor[] editors = myFileEditorManager.getAllEditors(myVirtualFile); @@ -161,6 +161,7 @@ public class LineStatusTracker { } } + @CalledInAwt private void removeAnathema() { if (!myAnathemaThrown) return; myAnathemaThrown = false; @@ -174,15 +175,15 @@ public class LineStatusTracker { } } + @CalledInAwt public void setMode(@NotNull Mode mode) { - synchronized (myLock) { - if (myMode == mode) return; - myMode = mode; - reinstallRanges(); - } + if (myMode == mode) return; + myMode = mode; + reinstallRanges(); } @NotNull + @CalledInAwt private RangeHighlighter createHighlighter(@NotNull Range range) { myApplication.assertIsDispatchThread(); @@ -225,31 +226,35 @@ public class LineStatusTracker { } public void release() { - synchronized (myLock) { - myReleased = true; - if (myDocumentListener != null) { - myDocument.removeDocumentListener(myDocumentListener); - } + myApplication.runReadAction(new Runnable() { + @Override + public void run() { + if (myReleased) return; + myReleased = true; + if (myDocumentListener != null) { + myDocument.removeDocumentListener(myDocumentListener); + } - if (myApplication.isDispatchThread()) { - removeAnathema(); - removeHighlightersFromMarkupModel(); + if (myApplication.isDispatchThread()) { + removeAnathema(); + removeHighlightersFromMarkupModel(); + } + else { + invalidateRanges(); + myApplication.invokeLater(new Runnable() { + @Override + public void run() { + removeAnathema(); + removeHighlightersFromMarkupModel(); + } + }); + } } - else { - invalidateRanges(); - myApplication.invokeLater(new Runnable() { - @Override - public void run() { - removeAnathema(); - removeHighlightersFromMarkupModel(); - } - }); - } - } + }); } @NotNull - Project getProject() { + public Project getProject() { return myProject; } @@ -269,51 +274,46 @@ public class LineStatusTracker { } @NotNull + @CalledWithReadLock public List getRanges() { - synchronized (myLock) { - return myRanges; - } + return Collections.unmodifiableList(myRanges); } + @CalledInAwt public void startBulkUpdate() { - synchronized (myLock) { - if (myReleased) return; + if (myReleased) return; - myBulkUpdate = true; - removeAnathema(); - removeHighlightersFromMarkupModel(); - } + myBulkUpdate = true; + removeAnathema(); + removeHighlightersFromMarkupModel(); } + @CalledInAwt private void removeHighlightersFromMarkupModel() { myApplication.assertIsDispatchThread(); - synchronized (myLock) { - for (Range range : myRanges) { - if (range.getHighlighter() != null) { - range.getHighlighter().dispose(); - } - range.invalidate(); + for (Range range : myRanges) { + if (range.getHighlighter() != null) { + range.getHighlighter().dispose(); } - myRanges.clear(); + range.invalidate(); } + myRanges.clear(); } + @CalledWithReadLock private void invalidateRanges() { - synchronized (myLock) { - for (Range range : myRanges) { - range.invalidate(); - } + for (Range range : myRanges) { + range.invalidate(); } } + @CalledInAwt public void finishBulkUpdate() { - synchronized (myLock) { - if (myReleased) return; + if (myReleased) return; - myBulkUpdate = false; - reinstallRanges(); - } + myBulkUpdate = false; + reinstallRanges(); } private void markFileUnchanged() { @@ -321,11 +321,7 @@ public class LineStatusTracker { @Override public void run() { FileDocumentManager.getInstance().saveDocument(myDocument); - boolean stillEmpty; - synchronized (myLock) { - stillEmpty = myRanges.isEmpty(); - } - if (stillEmpty) { + if (myRanges.isEmpty()) { // file was modified, and now it's not -> dirty local change myVcsDirtyScopeManager.fileDirty(myVirtualFile); } @@ -346,27 +342,25 @@ public class LineStatusTracker { @Override public void beforeDocumentChange(DocumentEvent e) { myApplication.assertIsDispatchThread(); + if (myReleased) return; - synchronized (myLock) { - if (myReleased) return; - if (myBulkUpdate || mySuppressUpdate || myAnathemaThrown || !myInitialized) return; - assert myDocument == e.getDocument(); + if (myBulkUpdate || mySuppressUpdate || myAnathemaThrown || !myInitialized) return; + assert myDocument == e.getDocument(); - try { - myLine1 = myDocument.getLineNumber(e.getOffset()); - if (e.getOldLength() == 0) { - myBeforeChangedLines = 1; - } - else { - int line1 = myLine1; - int line2 = myDocument.getLineNumber(e.getOffset() + e.getOldLength()); - myBeforeChangedLines = line2 - line1 + 1; - } - - myBeforeTotalLines = getLineCount(myDocument); + try { + myLine1 = myDocument.getLineNumber(e.getOffset()); + if (e.getOldLength() == 0) { + myBeforeChangedLines = 1; } - catch (ProcessCanceledException ignore) { + else { + int line1 = myLine1; + int line2 = myDocument.getLineNumber(e.getOffset() + e.getOldLength()); + myBeforeChangedLines = line2 - line1 + 1; } + + myBeforeTotalLines = getLineCount(myDocument); + } + catch (ProcessCanceledException ignore) { } } @@ -374,32 +368,30 @@ public class LineStatusTracker { public void documentChanged(final DocumentEvent e) { myApplication.assertIsDispatchThread(); - synchronized (myLock) { - if (myReleased) return; - if (myBulkUpdate || mySuppressUpdate || myAnathemaThrown || !myInitialized) return; - assert myDocument == e.getDocument(); + if (myReleased) return; + if (myBulkUpdate || mySuppressUpdate || myAnathemaThrown || !myInitialized) return; + assert myDocument == e.getDocument(); - int afterChangedLines; - if (e.getNewLength() == 0) { - afterChangedLines = 1; - } - else { - int line1 = myLine1; - int line2 = myDocument.getLineNumber(e.getOffset() + e.getNewLength()); - afterChangedLines = line2 - line1 + 1; - } - - int linesShift = afterChangedLines - myBeforeChangedLines; - - int line1 = myLine1; - int line2 = line1 + myBeforeChangedLines; - - int[] fixed = fixRanges(e, line1, line2); - line1 = fixed[0]; - line2 = fixed[1]; - - doUpdateRanges(line1, line2, linesShift, myBeforeTotalLines); + int afterChangedLines; + if (e.getNewLength() == 0) { + afterChangedLines = 1; } + else { + int line1 = myLine1; + int line2 = myDocument.getLineNumber(e.getOffset() + e.getNewLength()); + afterChangedLines = line2 - line1 + 1; + } + + int linesShift = afterChangedLines - myBeforeChangedLines; + + int line1 = myLine1; + int line2 = line1 + myBeforeChangedLines; + + int[] fixed = fixRanges(e, line1, line2); + line1 = fixed[0]; + line2 = fixed[1]; + + doUpdateRanges(line1, line2, linesShift, myBeforeTotalLines); } } @@ -523,10 +515,10 @@ public class LineStatusTracker { return Collections.emptyList(); } if (changedLine1 == changedLine2) { - return Collections.singletonList(new Range(changedLine1, changedLine2, vcsLine1, vcsLine2, Range.DELETED)); + return Collections.singletonList(new Range(changedLine1, changedLine2, vcsLine1, vcsLine2)); } if (vcsLine1 == vcsLine2) { - return Collections.singletonList(new Range(changedLine1, changedLine2, vcsLine1, vcsLine2, Range.INSERTED)); + return Collections.singletonList(new Range(changedLine1, changedLine2, vcsLine1, vcsLine2)); } List lines = new DocumentWrapper(myDocument).getLines(changedLine1, changedLine2 - 1); @@ -676,109 +668,73 @@ public class LineStatusTracker { } @Nullable - Range getNextRange(final Range range) { - synchronized (myLock) { - final int index = myRanges.indexOf(range); - if (index == myRanges.size() - 1) return null; - return myRanges.get(index + 1); - } + @CalledWithReadLock + public Range getNextRange(Range range) { + final int index = myRanges.indexOf(range); + if (index == myRanges.size() - 1) return null; + return myRanges.get(index + 1); } @Nullable - Range getPrevRange(final Range range) { - synchronized (myLock) { - final int index = myRanges.indexOf(range); - if (index <= 0) return null; - return myRanges.get(index - 1); - } + @CalledWithReadLock + public Range getPrevRange(Range range) { + final int index = myRanges.indexOf(range); + if (index <= 0) return null; + return myRanges.get(index - 1); } @Nullable - public Range getNextRange(final int line) { - synchronized (myLock) { - for (Range range : myRanges) { - if (line < range.getLine2() && !range.isSelectedByLine(line)) { - return range; - } + @CalledWithReadLock + public Range getNextRange(int line) { + for (Range range : myRanges) { + if (line < range.getLine2() && !range.isSelectedByLine(line)) { + return range; } - return null; } + return null; } @Nullable - public Range getPrevRange(final int line) { - synchronized (myLock) { - for (int i = myRanges.size() - 1; i >= 0; i--) { - Range range = myRanges.get(i); - if (line > range.getLine1() && !range.isSelectedByLine(line)) { - return range; - } + @CalledWithReadLock + public Range getPrevRange(int line) { + for (int i = myRanges.size() - 1; i >= 0; i--) { + Range range = myRanges.get(i); + if (line > range.getLine1() && !range.isSelectedByLine(line)) { + return range; } - return null; } + return null; } @Nullable - public Range getRangeForLine(final int line) { - synchronized (myLock) { - for (final Range range : myRanges) { - if (range.isSelectedByLine(line)) return range; - } - return null; + @CalledWithReadLock + public Range getRangeForLine(int line) { + for (final Range range : myRanges) { + if (range.isSelectedByLine(line)) return range; } + return null; } + @CalledWithWriteLock private void doRollbackRange(@NotNull Range range) { - if (range.getType() == Range.MODIFIED) { - TextRange currentTextRange = getCurrentTextRange(range); - int offset1 = currentTextRange.getStartOffset(); - int offset2 = currentTextRange.getEndOffset(); - - CharSequence vcsContent = getVcsContent(range); - myDocument.replaceString(offset1, offset2, vcsContent); - } - else if (range.getType() == Range.INSERTED) { - TextRange currentTextRange = getCurrentTextRange(range); - int offset1 = currentTextRange.getStartOffset(); - int offset2 = currentTextRange.getEndOffset(); - - if (offset1 > 0) { - offset1--; - } - else if (offset2 < myDocument.getTextLength()) { - offset2++; - } - myDocument.deleteString(offset1, offset2); - } - else if (range.getType() == Range.DELETED) { - CharSequence content = getVcsContent(range); - if (range.getLine2() == getLineCount(myDocument)) { - myDocument.insertString(myDocument.getTextLength(), "\n" + content); - } - else { - myDocument.insertString(myDocument.getLineStartOffset(range.getLine2()), content + "\n"); - } - } - else { - throw new IllegalArgumentException("Unknown range type: " + range.getType()); - } + DiffUtil.applyModification(myDocument, range.getLine1(), range.getLine2(), myVcsDocument, range.getVcsLine1(), range.getVcsLine2()); } + @CalledWithWriteLock public void rollbackChanges(@NotNull Range range) { myApplication.assertWriteAccessAllowed(); - synchronized (myLock) { - if (myBulkUpdate) return; + if (myBulkUpdate) return; - if (!range.isValid()) { - LOG.warn("Rollback of invalid range"); - return; - } - - doRollbackRange(range); + if (!range.isValid()) { + LOG.warn("Rollback of invalid range"); + return; } + + doRollbackRange(range); } + @CalledWithWriteLock public void rollbackChanges(@NotNull final BitSet lines) { runBulkRollback(new Runnable() { @Override @@ -793,14 +749,7 @@ public class LineStatusTracker { break; } - boolean check; - if (range.getLine1() == range.getLine2()) { - check = lines.get(range.getLine1()); - } - else { - int next = lines.nextSetBit(range.getLine1()); - check = next != -1 && next < range.getLine2(); - } + boolean check = DiffUtil.isSelectedByLine(lines, range.getLine1(), range.getLine2()); if (check) { if (first == null) { @@ -829,6 +778,7 @@ public class LineStatusTracker { }); } + @CalledWithWriteLock public void rollbackAllChanges() { runBulkRollback(new Runnable() { @Override @@ -843,88 +793,74 @@ public class LineStatusTracker { }); } + @CalledWithWriteLock private void runBulkRollback(@NotNull Runnable task) { myApplication.assertWriteAccessAllowed(); - synchronized (myLock) { - if (myBulkUpdate) return; + if (myBulkUpdate) return; - try { - mySuppressUpdate = true; + try { + mySuppressUpdate = true; - task.run(); - } - catch (Error e) { - reinstallRanges(); - throw e; - } - catch (RuntimeException e) { - reinstallRanges(); - throw e; - } - finally { - mySuppressUpdate = false; - } + task.run(); + } + catch (Error e) { + reinstallRanges(); + throw e; + } + catch (RuntimeException e) { + reinstallRanges(); + throw e; + } + finally { + mySuppressUpdate = false; } } + @NotNull + @CalledWithReadLock + public CharSequence getCurrentContent(@NotNull Range range) { + TextRange textRange = getCurrentTextRange(range); + final int startOffset = textRange.getStartOffset(); + final int endOffset = textRange.getEndOffset(); + return myDocument.getImmutableCharSequence().subSequence(startOffset, endOffset); + } + + @NotNull + @CalledWithReadLock public CharSequence getVcsContent(@NotNull Range range) { - synchronized (myLock) { - TextRange textRange = getVcsRange(range); - final int startOffset = textRange.getStartOffset(); - final int endOffset = textRange.getEndOffset(); - return myVcsDocument.getCharsSequence().subSequence(startOffset, endOffset); - } + TextRange textRange = getVcsTextRange(range); + final int startOffset = textRange.getStartOffset(); + final int endOffset = textRange.getEndOffset(); + return myVcsDocument.getImmutableCharSequence().subSequence(startOffset, endOffset); } @NotNull - TextRange getCurrentTextRange(@NotNull Range range) { - myApplication.assertReadAccessAllowed(); - - synchronized (myLock) { - if (!range.isValid()) { - LOG.warn("Current TextRange of invalid range"); - } - - return getRange(range.getLine1(), range.getLine2(), myDocument); + @CalledWithReadLock + public TextRange getCurrentTextRange(@NotNull Range range) { + if (!range.isValid()) { + LOG.warn("Current TextRange of invalid range"); } + + return DiffUtil.getLinesRange(myDocument, range.getLine1(), range.getLine2()); } @NotNull - TextRange getVcsRange(@NotNull Range range) { - synchronized (myLock) { - if (!range.isValid()) { - LOG.warn("Vcs TextRange of invalid range"); - } - - return getRange(range.getVcsLine1(), range.getVcsLine2(), myVcsDocument); + @CalledWithReadLock + public TextRange getVcsTextRange(@NotNull Range range) { + if (!range.isValid()) { + LOG.warn("Vcs TextRange of invalid range"); } + + return DiffUtil.getLinesRange(myVcsDocument, range.getVcsLine1(), range.getVcsLine2()); } - /** - * Return affected range, without non-internal '\n' - * so if last line is not empty, the last symbol will be not '\n' - *

- * So we consider '\n' not as a part of line, but a separator between lines - */ @NotNull - private static TextRange getRange(int line1, int line2, @NotNull Document document) { - if (line1 == line2) { - int lineStartOffset = line1 < getLineCount(document) ? document.getLineStartOffset(line1) : document.getTextLength(); - return new TextRange(lineStartOffset, lineStartOffset); - } - else { - int startOffset = document.getLineStartOffset(line1); - int endOffset = document.getLineEndOffset(line2 - 1); - return new TextRange(startOffset, endOffset); - } - } - - public static LineStatusTracker createOn(@NotNull VirtualFile virtualFile, @NotNull final Document doc, final Project project, + public static LineStatusTracker createOn(@NotNull VirtualFile virtualFile, @NotNull final Document document, final Project project, @NotNull Mode mode) { - final Document document = new DocumentImpl("", true); - document.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); - return new LineStatusTracker(doc, document, project, virtualFile, mode); + final Document vcsDocument = new DocumentImpl("", true); + vcsDocument.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); + return new LineStatusTracker(document, vcsDocument, project, virtualFile, mode); } public static class RevisionPack { @@ -965,13 +901,9 @@ public class LineStatusTracker { } } - public static class CanNotCalculateDiffPanel extends EditorNotificationPanel { + private static class CanNotCalculateDiffPanel extends EditorNotificationPanel { public CanNotCalculateDiffPanel() { myLabel.setText("Can not highlight changed lines. File is too big and there are too many changes."); } } - - private static int getLineCount(@NotNull Document document) { - return Math.max(document.getLineCount(), 1); - } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerDrawing.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerDrawing.java index ed6db0e54467..d29e373cca59 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerDrawing.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerDrawing.java @@ -18,6 +18,14 @@ package com.intellij.openapi.vcs.ex; import com.intellij.codeInsight.hint.EditorFragmentComponent; import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.hint.HintManagerImpl; +import com.intellij.diff.comparison.ByWord; +import com.intellij.diff.comparison.ComparisonPolicy; +import com.intellij.diff.comparison.DiffTooBigException; +import com.intellij.diff.fragments.DiffFragment; +import com.intellij.diff.util.BackgroundTaskUtil; +import com.intellij.diff.util.DiffDrawUtil; +import com.intellij.diff.util.DiffUtil; +import com.intellij.diff.util.TextDiffType; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionUtil; import com.intellij.openapi.diff.DiffColors; @@ -32,8 +40,11 @@ import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; import com.intellij.openapi.editor.markup.ActiveGutterRenderer; import com.intellij.openapi.editor.markup.LineMarkerRenderer; +import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vcs.actions.ShowNextChangeMarkerAction; import com.intellij.openapi.vcs.actions.ShowPrevChangeMarkerAction; import com.intellij.openapi.vfs.VirtualFile; @@ -41,6 +52,7 @@ import com.intellij.ui.ColoredSideBorder; import com.intellij.ui.HintHint; import com.intellij.ui.HintListener; import com.intellij.ui.LightweightHint; +import com.intellij.util.Function; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -49,6 +61,7 @@ import javax.swing.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; import java.util.EventObject; import java.util.List; @@ -256,7 +269,7 @@ public class LineStatusTrackerDrawing { component.add(toolbarPanel, BorderLayout.NORTH); - + final List highlighters = new ArrayList(); if (range.getType() != Range.INSERTED) { final DocumentEx doc = (DocumentEx)tracker.getVcsDocument(); final EditorEx uEditor = (EditorEx)EditorFactory.getInstance().createViewer(doc, tracker.getProject()); @@ -265,6 +278,36 @@ public class LineStatusTrackerDrawing { EditorHighlighterFactory.getInstance().createEditorHighlighter(tracker.getProject(), getFileName(tracker.getDocument())); uEditor.setHighlighter(highlighter); + try { + if (range.getType() == Range.MODIFIED) { + final CharSequence vcsContent = tracker.getVcsContent(range); + final CharSequence currentContent = tracker.getCurrentContent(range); + + List diff = BackgroundTaskUtil.tryComputeFast(new Function>() { + @Override + public List fun(ProgressIndicator indicator) { + return ByWord.compare(vcsContent, currentContent, ComparisonPolicy.DEFAULT, indicator); + } + }, Registry.intValue("diff.status.tracker.byword.delay")); + if (diff == null) throw new DiffTooBigException(); + + int vcsStartShift = tracker.getVcsTextRange(range).getStartOffset(); + int currentStartShift = tracker.getCurrentTextRange(range).getStartOffset(); + for (DiffFragment fragment : diff) { + int vcsStart = vcsStartShift + fragment.getStartOffset1(); + int vcsEnd = vcsStartShift + fragment.getEndOffset1(); + int currentStart = currentStartShift + fragment.getStartOffset2(); + int currentEnd = currentStartShift + fragment.getEndOffset2(); + TextDiffType type = DiffUtil.getDiffType(fragment); + + DiffDrawUtil.createInlineHighlighter(uEditor, vcsStart, vcsEnd, type); + highlighters.add(DiffDrawUtil.createInlineHighlighter(editor, currentStart, currentEnd, type)); + } + } + } + catch (DiffTooBigException ignore) { + } + final EditorFragmentComponent editorFragmentComponent = EditorFragmentComponent.createEditorFragmentComponent(uEditor, range.getVcsLine1(), range.getVcsLine2(), false, false); @@ -284,6 +327,9 @@ public class LineStatusTrackerDrawing { actionList.remove(copyRange); actionList.remove(localShowPrevAction); actionList.remove(localShowNextAction); + for (RangeHighlighter highlighter : highlighters) { + highlighter.dispose(); + } } }; hint.addHintListener(closeListener); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/Range.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/Range.java index 8b87fbf5c23e..1840022ce0d2 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/Range.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/Range.java @@ -15,18 +15,14 @@ */ package com.intellij.openapi.vcs.ex; +import com.intellij.diff.util.DiffUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.util.diff.Diff; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; -/** - * author: lesya - */ public class Range { private static final Logger LOG = Logger.getInstance(Range.class); public static final byte EQUAL = 0; @@ -41,81 +37,64 @@ public class Range { private int myLine2; private final int myVcsLine1; private final int myVcsLine2; - private final byte myType; - @Nullable private RangeHighlighter myRangeHighlighter; @Nullable private final List myInnerRanges; + @Nullable private RangeHighlighter myRangeHighlighter; private boolean myValid = true; public Range(@NotNull Range range) { - this(range.getLine1(), range.getLine2(), range.getVcsLine1(), range.getVcsLine2(), range.getType()); + this(range.getLine1(), range.getLine2(), range.getVcsLine1(), range.getVcsLine2()); } - public Range(int line1, int line2, int vcsLine1, int vcsLine2, byte type) { - this(line1, line2, vcsLine1, vcsLine2, type, null); + public Range(int line1, int line2, int vcsLine1, int vcsLine2) { + this(line1, line2, vcsLine1, vcsLine2, null); } - public Range(int line1, int line2, int vcsLine1, int vcsLine2, byte type, @Nullable List innerRanges) { + public Range(int line1, int line2, int vcsLine1, int vcsLine2, @Nullable List innerRanges) { + assert line1 != line2 || vcsLine1 != vcsLine2; + myLine1 = line1; myLine2 = line2; myVcsLine1 = vcsLine1; myVcsLine2 = vcsLine2; - myType = type; myInnerRanges = innerRanges; } public int hashCode() { - return myVcsLine1 ^ myVcsLine2 ^ myType ^ myLine1 ^ myLine2; + return myVcsLine1 ^ myVcsLine2 ^ myLine1 ^ myLine2; } - public boolean equals(Object object) { - if (!(object instanceof Range)) return false; - Range other = (Range)object; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - if (myInnerRanges != null) { - if (other.myInnerRanges == null) return false; - if (myInnerRanges.size() != other.myInnerRanges.size()) return false; + Range range = (Range)o; - for (int i = 0; i < myInnerRanges.size(); i++) { - if (!myInnerRanges.get(i).equals(other.myInnerRanges.get(i))) return false; - } - } - else { - if (other.myInnerRanges != null) return false; + if (myLine1 != range.myLine1) return false; + if (myLine2 != range.myLine2) return false; + if (myVcsLine1 != range.myVcsLine1) return false; + if (myVcsLine2 != range.myVcsLine2) return false; + + if (myInnerRanges == null) return range.myInnerRanges == null; + if (range.myInnerRanges == null) return false; + + if (myInnerRanges.size() != range.myInnerRanges.size()) return false; + for (int i = 0; i < myInnerRanges.size(); i++) { + if (!myInnerRanges.get(i).equals(range.myInnerRanges.get(i))) return false; } - return - (myVcsLine1 == other.myVcsLine1) - && (myVcsLine2 == other.myVcsLine2) - && (myLine1 == other.myLine1) - && (myLine2 == other.myLine2) - && (myType == other.myType); + return true; } public String toString() { - return String.format("%s, %s, %s, %s, %s", myLine1, myLine2, myVcsLine1, myVcsLine2, getTypeName()); - } - - @NonNls - private String getTypeName() { - switch (myType) { - case MODIFIED: - return "MODIFIED"; - case INSERTED: - return "INSERTED"; - case DELETED: - return "DELETED"; - } - return "UNKNOWN"; + return String.format("%s, %s, %s, %s", myLine1, myLine2, myVcsLine1, myVcsLine2); } public byte getType() { - return myType; - } - - public int getUpToDateRangeLength() { - return myVcsLine2 - myVcsLine1; + if (myLine1 == myLine2) return DELETED; + if (myVcsLine1 == myVcsLine2) return INSERTED; + return MODIFIED; } public void shift(int shift) { @@ -150,15 +129,11 @@ public class Range { return myVcsLine2; } - public boolean rightBefore(@NotNull Range range) { - return myLine2 == range.myLine1; - } - public boolean hasHighlighter() { return myRangeHighlighter != null; } - public void setHighlighter(RangeHighlighter highlighter) { + public void setHighlighter(@Nullable RangeHighlighter highlighter) { myRangeHighlighter = highlighter; } @@ -210,8 +185,8 @@ public class Range { InnerRange range = (InnerRange)o; - if (myLine2 != range.myLine2) return false; if (myLine1 != range.myLine1) return false; + if (myLine2 != range.myLine2) return false; if (myType != range.myType) return false; return true; @@ -226,22 +201,7 @@ public class Range { } public String toString() { - return String.format("%s, %s, %s", myLine1, myLine2, getTypeName()); - } - - @NonNls - private String getTypeName() { - switch (myType) { - case MODIFIED: - return "MODIFIED"; - case INSERTED: - return "INSERTED"; - case DELETED: - return "DELETED"; - case EQUAL: - return "EQUAL"; - } - return "UNKNOWN"; + return String.format("%s, %s, %s", myLine1, myLine2, getTypeName(myType)); } } @@ -249,12 +209,21 @@ public class Range { * Check, if caret at is corresponds to the current range */ public boolean isSelectedByLine(int line) { - if (getType() == DELETED && line == myLine1) { - return true; + return DiffUtil.isSelectedByLine(line, myLine1, myLine2); + } + + @NotNull + private static String getTypeName(byte type) { + switch (type) { + case MODIFIED: + return "MODIFIED"; + case INSERTED: + return "INSERTED"; + case DELETED: + return "DELETED"; + case EQUAL: + return "EQUAL"; } - else if (line >= myLine1 && line < myLine2) { - return true; - } - return false; + return "UNKNOWN(" + type + ")"; } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java index ccb5b3390a30..18cf26ec9be1 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java @@ -101,15 +101,13 @@ public class RangesBuilder { } private static Range createOn(@NotNull Diff.Change change, int shift, int vcsShift) { - byte type = getChangeType(change); - int offset1 = shift + change.line1; int offset2 = offset1 + change.inserted; int uOffset1 = vcsShift + change.line0; int uOffset2 = uOffset1 + change.deleted; - return new Range(offset1, offset2, uOffset1, uOffset2, type); + return new Range(offset1, offset2, uOffset1, uOffset2); } private static Range createOnSmart(@NotNull Diff.Change change, @@ -126,7 +124,7 @@ public class RangesBuilder { int uOffset2 = uOffset1 + change.deleted; if (type != Range.MODIFIED) { - return new Range(offset1, offset2, uOffset1, uOffset2, type, Collections.singletonList(new Range.InnerRange(offset1, offset2, type))); + return new Range(offset1, offset2, uOffset1, uOffset2, Collections.singletonList(new Range.InnerRange(offset1, offset2, type))); } LineWrapper[] lines1 = new LineWrapper[change.deleted]; @@ -169,7 +167,7 @@ public class RangesBuilder { inner.add(new Range.InnerRange(innerStart, innerEnd, innerType)); } - return new Range(offset1, offset2, uOffset1, uOffset2, type, inner); + return new Range(offset1, offset2, uOffset1, uOffset2, inner); } private static byte getChangeType(@NotNull Diff.Change change) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/ShowLineStatusRangeDiffAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/ShowLineStatusRangeDiffAction.java index e8418e230fac..67ba09038d7f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/ShowLineStatusRangeDiffAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/ShowLineStatusRangeDiffAction.java @@ -16,7 +16,6 @@ package com.intellij.openapi.vcs.ex; import com.intellij.diff.DiffContentFactory; -import com.intellij.diff.DiffDialogHints; import com.intellij.diff.DiffManager; import com.intellij.diff.actions.DocumentFragmentContent; import com.intellij.diff.contents.DiffContent; @@ -53,7 +52,7 @@ public class ShowLineStatusRangeDiffAction extends BaseLineStatusRangeAction { Range range = expand(myRange, myLineStatusTracker.getDocument(), myLineStatusTracker.getVcsDocument()); DiffContent vcsContent = createDiffContent(myLineStatusTracker.getVcsDocument(), - myLineStatusTracker.getVcsRange(range), + myLineStatusTracker.getVcsTextRange(range), null); DiffContent currentContent = createDiffContent(myLineStatusTracker.getDocument(), myLineStatusTracker.getCurrentTextRange(range), @@ -81,6 +80,6 @@ public class ShowLineStatusRangeDiffAction extends BaseLineStatusRangeAction { int uOffset1 = range.getVcsLine1() - (canExpandBefore ? 1 : 0); int offset2 = range.getLine2() + (canExpandAfter ? 1 : 0); int uOffset2 = range.getVcsLine2() + (canExpandAfter ? 1 : 0); - return new Range(offset1, offset2, uOffset1, uOffset2, range.getType()); + return new Range(offset1, offset2, uOffset1, uOffset2); } } \ No newline at end of file diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java index 6de1143b6f8a..265e7fe38871 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java @@ -62,6 +62,7 @@ import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.CalledInAwt; import java.util.Map; @@ -208,6 +209,7 @@ public class LineStatusTrackerManager implements ProjectComponent, LineStatusTra } } + @CalledInAwt public void updateSettings() { synchronized (myLock) { LineStatusTracker.Mode mode = getMode(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java index 5d049f3e9c14..19f28961b96a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java @@ -44,7 +44,7 @@ public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvide } return calcLineNumber(tracker, currentNumber); } - + public boolean isRangeChanged(final int start, final int end) { LineStatusTracker tracker = LineStatusTrackerManager.getInstance(myProject).getLineStatusTracker(myDocument); if (tracker == null) { @@ -60,7 +60,7 @@ public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvide } return false; } - + private static boolean lineInRange(final Range range, final int currentNumber) { return range.getLine1() <= currentNumber && range.getLine2() >= currentNumber; } @@ -106,23 +106,23 @@ public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvide private static int calcLineNumber(LineStatusTracker tracker, int currentNumber){ if (tracker == null) return -1; - List ranges = tracker.getRanges(); + List ranges = tracker.getRanges(); int result = currentNumber; - for (final Object range1 : ranges) { - Range range = (Range)range1; - int startOffset = range.getLine1(); - int endOffset = range.getLine2(); + for (final Range range : ranges) { + int startLine = range.getLine1(); + int endLine = range.getLine2(); - if ((startOffset <= currentNumber) && (endOffset > currentNumber)) { + if ((startLine <= currentNumber) && (endLine > currentNumber)) { return ABSENT_LINE_NUMBER; } - if (endOffset > currentNumber) return result; + if (endLine > currentNumber) return result; - int currentRangeLength = endOffset - startOffset; + int currentRangeLength = endLine - startLine; + int vcsRangeLength = range.getVcsLine2() - range.getVcsLine1(); - result += range.getUpToDateRangeLength() - currentRangeLength; + result += vcsRangeLength - currentRangeLength; } return result; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrFileStub.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrFileStub.java index 4d482477da42..abe676008c09 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrFileStub.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrFileStub.java @@ -15,7 +15,6 @@ */ package org.jetbrains.plugins.groovy.lang.psi.stubs; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.stubs.PsiFileStubImpl; import com.intellij.psi.tree.IStubFileElementType; import com.intellij.util.ArrayUtil; @@ -34,7 +33,7 @@ public class GrFileStub extends PsiFileStubImpl { public GrFileStub(GroovyFile file) { super(file); - myName = StringRef.fromString(StringUtil.trimEnd(file.getName(), ".groovy")); + myName = StringRef.fromString(file.getViewProvider().getVirtualFile().getNameWithoutExtension()); isScript = file.isScript(); final GrPackageDefinition definition = file.getPackageDefinition(); if (definition != null) { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java index 8ccfe21915f8..1942f869f58f 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java @@ -58,7 +58,7 @@ public class GrStubFileElementType extends IStubFileElementType { @Override public int getStubVersion() { - return super.getStubVersion() + 21; + return super.getStubVersion() + 22; } @Override diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyFileTypeLoader.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyFileTypeLoader.java index 734cae7b3470..16cb6a7b1142 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyFileTypeLoader.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyFileTypeLoader.java @@ -32,8 +32,7 @@ public class GroovyFileTypeLoader extends FileTypeFactory{ public static Set getCustomGroovyScriptExtensions() { final LinkedHashSet strings = new LinkedHashSet(); strings.add("gdsl"); - strings.add("gpp"); - strings.add("grunit"); + strings.add("gy"); for (GroovyScriptTypeDetector ep : GroovyScriptTypeDetector.EP_NAME.getExtensions()) { Collections.addAll(strings, ep.getExtensions()); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyCodeFragmentFactory.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyCodeFragmentFactory.java index b71a3d3d6f93..c1f27061eca6 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyCodeFragmentFactory.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyCodeFragmentFactory.java @@ -175,7 +175,7 @@ public class GroovyCodeFragmentFactory extends CodeFragmentFactory { "java.lang.Object |res = ((groovy.lang.MetaClassImpl)|emc).invokeStaticMethod(|clazz, \"" ).append(EVAL_NAME).append("\", |resVals);\n"); } - javaText.append("if (|res instanceof java.lang.Boolean) ((java.lang.Boolean) |res).booleanValue() else |res"); + javaText.append("|res"); final PsiElementFactory factory = JavaPsiFacade.getInstance(toEval.getProject()).getElementFactory(); diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java index 3460e1404463..b71531f51d77 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java @@ -15,6 +15,8 @@ */ package org.jetbrains.java.decompiler.modules.decompiler.stats; +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; @@ -84,11 +86,20 @@ public class SynchronizedStatement extends Statement { buf.append(ExprProcessor.jmpWrapper(body, indent + 1, true, tracer)); buf.appendIndent(indent).append("}").appendLineSeparator(); + mapMonitorExitInstr(tracer); tracer.incrementCurrentSourceLine(); return buf; } + private void mapMonitorExitInstr(BytecodeMappingTracer tracer) { + BasicBlock block = body.getBasichead().getBlock(); + if (!block.getSeq().isEmpty() && block.getLastInstruction().opcode == CodeConstants.opc_monitorexit) { + Integer offset = block.getOldOffset(block.size() - 1); + if (offset > -1) tracer.addMapping(offset); + } + } + public void initExprents() { headexprent.set(0, first.getExprents().remove(first.getExprents().size() - 1)); } diff --git a/plugins/java-decompiler/engine/testData/classes/pkg/TestSynchronizedMapping.class b/plugins/java-decompiler/engine/testData/classes/pkg/TestSynchronizedMapping.class index 4691cfd73b71..e320211ee8c1 100644 Binary files a/plugins/java-decompiler/engine/testData/classes/pkg/TestSynchronizedMapping.class and b/plugins/java-decompiler/engine/testData/classes/pkg/TestSynchronizedMapping.class differ diff --git a/plugins/java-decompiler/engine/testData/results/TestSynchronizedMapping.dec b/plugins/java-decompiler/engine/testData/results/TestSynchronizedMapping.dec index 82b553b2bfdf..84ad48a1e660 100644 --- a/plugins/java-decompiler/engine/testData/results/TestSynchronizedMapping.dec +++ b/plugins/java-decompiler/engine/testData/results/TestSynchronizedMapping.dec @@ -3,33 +3,37 @@ package pkg; public class TestSynchronizedMapping { public int test(int var1) { synchronized(this) {// 8 - return var1++;// 9 - } + ++var1;// 9 + }// 10 + + return var1++;// 11 } public void test2(String var1) { - System.out.println(var1);// 14 - }// 15 + System.out.println(var1);// 15 + }// 16 } class 'pkg/TestSynchronizedMapping' { method 'test (I)I' { 3 4 - 5 5 - a 5 + 4 5 + 8 6 + 12 8 + 15 8 } method 'test2 (Ljava/lang/String;)V' { - 0 10 - 4 10 - 7 11 + 0 12 + 4 12 + 7 13 } } Lines mapping: 8 <-> 5 9 <-> 6 -14 <-> 11 -15 <-> 12 -Not mapped: -10 +10 <-> 7 +11 <-> 9 +15 <-> 13 +16 <-> 14 diff --git a/plugins/java-decompiler/engine/testData/src/pkg/TestSynchronizedMapping.java b/plugins/java-decompiler/engine/testData/src/pkg/TestSynchronizedMapping.java index 37f028236153..8effb75f24af 100644 --- a/plugins/java-decompiler/engine/testData/src/pkg/TestSynchronizedMapping.java +++ b/plugins/java-decompiler/engine/testData/src/pkg/TestSynchronizedMapping.java @@ -6,8 +6,9 @@ import java.lang.Runnable; public class TestSynchronizedMapping { public int test(int a) { synchronized (this) { - return a++; + a++; } + return a++; } public void test2(String a) { diff --git a/python/helpers/profiler/run_profiler.py b/python/helpers/profiler/run_profiler.py index dc60a201d37b..1b01dea126fc 100644 --- a/python/helpers/profiler/run_profiler.py +++ b/python/helpers/profiler/run_profiler.py @@ -81,10 +81,11 @@ class Profiler(object): self.start_profiling() - pydev_imports.execfile(file, globals, globals) # execute the script - - self.stop_profiling() - self.save_snapshot(0, generate_snapshot_filepath(base_snapshot_path, remote_run), remote_run) + try: + pydev_imports.execfile(file, globals, globals) # execute the script + finally: + self.stop_profiling() + self.save_snapshot(0, generate_snapshot_filepath(base_snapshot_path, remote_run), remote_run) def start_profiling(self): self.profiling_backend.enable() diff --git a/python/helpers/pydev/pydev_run_in_console.py b/python/helpers/pydev/pydev_run_in_console.py index 4224dd1ca999..9d5ac3b207fa 100644 --- a/python/helpers/pydev/pydev_run_in_console.py +++ b/python/helpers/pydev/pydev_run_in_console.py @@ -23,6 +23,7 @@ def run_file(file, globals=None, locals=None): if locals is None: locals = globals + sys.path.insert(0, os.path.split(file)[0]) print('Running %s'%file) pydev_imports.execfile(file, globals, locals) # execute the script diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java index 7eed689b46ee..0ab8651f56fd 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java @@ -61,6 +61,7 @@ import com.jetbrains.python.console.PythonDebugLanguageConsoleView; import com.jetbrains.python.console.pydev.PydevCompletionVariant; import com.jetbrains.python.debugger.pydev.*; import com.jetbrains.python.psi.PyFunction; +import com.jetbrains.python.psi.PyImportElement; import com.jetbrains.python.psi.resolve.PyResolveUtil; import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyType; @@ -854,7 +855,11 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr PyResolveUtil.scopeCrawlUp(new PsiScopeProcessor() { @Override public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) { - elementRef.set(element); + if (!(element instanceof PyImportElement)) { + if (elementRef.isNull()) { + elementRef.set(element); + } + } return false; } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java index c8ac88af9da1..d6568240ecf4 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java @@ -18,7 +18,10 @@ package com.jetbrains.python.inspections.quickfix; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; @@ -26,16 +29,15 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; /** * For: * class B(A): - * def __init__(self): - * A.__init__(self) # inserted - * print "Constructor B was called" - * + * def __init__(self): + * A.__init__(self) # inserted + * print "Constructor B was called" + *

* User: catherine */ public class AddCallSuperQuickFix implements LocalQuickFix { @@ -52,7 +54,7 @@ public class AddCallSuperQuickFix implements LocalQuickFix { } public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) { - PyFunction problemFunction = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PyFunction.class); + final PyFunction problemFunction = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PyFunction.class); if (problemFunction == null) return; final StringBuilder superCall = new StringBuilder(); final PyClass klass = problemFunction.getContainingClass(); @@ -63,35 +65,56 @@ public class AddCallSuperQuickFix implements LocalQuickFix { final PyClass superClass = superClasses[0]; final PyFunction superInit = superClass.findMethodByName(PyNames.INIT, true); if (superInit == null) return; - boolean addComma = true; + final boolean addComma; if (klass.isNewStyleClass()) { addComma = false; - if (LanguageLevel.forElement(klass).isPy3K()) + if (LanguageLevel.forElement(klass).isPy3K()) { superCall.append("super().__init__("); - else + } + else { superCall.append("super(").append(klass.getName()).append(", self).__init__("); + } } else { + addComma = true; superCall.append(superClass.getName()); superCall.append(".__init__(self"); } - StringBuilder newFunction = new StringBuilder("def __init__(self"); + final StringBuilder newFunction = new StringBuilder("def __init__(self"); - buildParameterList(problemFunction, superInit, superCall, newFunction, addComma); + final Couple> couple = buildNewFunctionParamsAndSuperInitCallArgs(problemFunction, superInit); + final List newParameters = couple.getFirst(); + if (!newParameters.isEmpty()) { + newFunction.append(", "); + } + StringUtil.join(newParameters, ", ", newFunction); + newFunction.append(")"); + if (problemFunction.getAnnotation() != null) { + newFunction.append(problemFunction.getAnnotation().getText()); + } + newFunction.append(":\n\t"); + + final List superCallArguments = couple.getSecond(); + if (addComma && !superCallArguments.isEmpty()) { + superCall.append(", "); + } + StringUtil.join(superCallArguments, ", ", superCall); superCall.append(")"); + final PyStatementList statementList = problemFunction.getStatementList(); PyExpression docstring = null; final PyStatement[] statements = statementList.getStatements(); if (statements.length != 0 && statements[0] instanceof PyExpressionStatement) { - PyExpressionStatement st = (PyExpressionStatement)statements[0]; - if (st.getExpression() instanceof PyStringLiteralExpression) + final PyExpressionStatement st = (PyExpressionStatement)statements[0]; + if (st.getExpression() instanceof PyStringLiteralExpression) { docstring = st.getExpression(); + } } - newFunction.append("):\n\t"); - if (docstring != null) + if (docstring != null) { newFunction.append(docstring.getText()).append("\n\t"); + } newFunction.append(superCall).append("\n\t"); boolean first = true; for (PyStatement statement : statements) { @@ -102,89 +125,249 @@ public class AddCallSuperQuickFix implements LocalQuickFix { newFunction.append(statement.getText()).append("\n\t"); } - problemFunction.replace( - PyElementGenerator.getInstance(project).createFromText(LanguageLevel.forElement(problemFunction), PyFunction.class, - newFunction.toString())); + final PyElementGenerator generator = PyElementGenerator.getInstance(project); + problemFunction.replace(generator.createFromText(LanguageLevel.forElement(problemFunction), PyFunction.class, newFunction.toString())); } - private static void buildParameterList(@NotNull final PyFunction problemFunction, - @NotNull final PyFunction superInit, - @NotNull final StringBuilder superCall, - @NotNull final StringBuilder newFunction, boolean addComma) { - final PyParameter[] parameters = problemFunction.getParameterList().getParameters(); - final List problemParams = new ArrayList(); - final List functionParams = new ArrayList(); - String starName = null; - String doubleStarName = null; - for (int i = 1; i != parameters.length; i++) { - PyParameter p = parameters[i]; - functionParams.add(p.getName()); - if (p.getText().startsWith("**")) { - doubleStarName = p.getText(); - continue; + @NotNull + private static Couple> buildNewFunctionParamsAndSuperInitCallArgs(@NotNull PyFunction origInit, + @NotNull PyFunction superInit) { + final List newFunctionParams = new ArrayList(); + final List superCallArgs = new ArrayList(); + + final ParametersInfo origInfo = new ParametersInfo(origInit.getParameterList()); + final ParametersInfo superInfo = new ParametersInfo(superInit.getParameterList()); + + // Required parameters (not-keyword) + for (PyParameter param : origInfo.getRequiredParameters()) { + newFunctionParams.add(param.getText()); + } + for (PyParameter param : superInfo.getRequiredParameters()) { + // Special case as if base class has constructor __init__((a, b), c) and + // subclass has constructor __init__(a, (b, c)) + final PyTupleParameter tupleParam = param.getAsTuple(); + if (tupleParam != null) { + final List uniqueNames = collectParameterNames(tupleParam); + final boolean hasDuplicates = uniqueNames.removeAll(origInfo.getAllParameterNames()); + if (hasDuplicates) { + newFunctionParams.addAll(uniqueNames); + } + else { + newFunctionParams.add(param.getText()); + } + // Retain original structure of tuple parameter. + // Note that tuple parameters cannot have annotations or nested default values, so it's syntactically safe + superCallArgs.add(param.getText()); } - if (p.getText().startsWith("*")) { - starName = p.getText(); - continue; + else { + if (!origInfo.getAllParameterNames().contains(param.getName())) { + newFunctionParams.add(param.getText()); + } + superCallArgs.add(param.getName()); } - if (p.getDefaultValue() != null) { - problemParams.add(p.getText()); - continue; - } - newFunction.append(",").append(p.getText()); } - addParametersFromSuper(superInit, superCall, newFunction, addComma, problemParams, functionParams, starName, doubleStarName); + // Optional parameters (not-keyword) + for (PyParameter param : origInfo.getOptionalParameters()) { + newFunctionParams.add(param.getText()); + } + + // Positional vararg + PyParameter starredParam = null; + if (origInfo.getPositionalContainerParameter() != null) { + starredParam = origInfo.getPositionalContainerParameter(); + } + else if (superInfo.getPositionalContainerParameter() != null) { + starredParam = superInfo.getPositionalContainerParameter(); + } + else if (origInfo.getSingleStarParameter() != null) { + starredParam = origInfo.getSingleStarParameter(); + } + else if (superInfo.getSingleStarParameter() != null) { + starredParam = superInfo.getSingleStarParameter(); + } + if (starredParam != null) { + newFunctionParams.add(starredParam.getText()); + if (superInfo.getPositionalContainerParameter() != null) { + superCallArgs.add("*" + starredParam.getName()); + } + } + + // Required keyword-only parameters + for (PyParameter param : origInfo.getRequiredKeywordOnlyParameters()) { + newFunctionParams.add(param.getText()); + } + for (PyParameter param : superInfo.getRequiredKeywordOnlyParameters()) { + if (!origInfo.getAllParameterNames().contains(param.getName())) { + newFunctionParams.add(param.getText()); + } + superCallArgs.add(param.getName() + "=" + param.getName()); + } + + // Optional keyword-only parameters + for (PyParameter param : origInfo.getOptionalKeywordOnlyParameters()) { + newFunctionParams.add(param.getText()); + } + + // Keyword vararg + PyParameter doubleStarredParam = null; + if (origInfo.getKeywordContainerParameter() != null) { + doubleStarredParam = origInfo.getKeywordContainerParameter(); + } + else if (superInfo.getKeywordContainerParameter() != null) { + doubleStarredParam = superInfo.getKeywordContainerParameter(); + } + if (doubleStarredParam != null) { + newFunctionParams.add(doubleStarredParam.getText()); + if (superInfo.getKeywordContainerParameter() != null) { + superCallArgs.add("**" + doubleStarredParam.getName()); + } + } + return Couple.of(newFunctionParams, superCallArgs); } - private static void addParametersFromSuper(@NotNull final PyFunction superInit, @NotNull final StringBuilder superCall, - @NotNull final StringBuilder newFunction, boolean addComma, - @NotNull final List problemParams, @NotNull final List functionParams, - @Nullable String starName, @Nullable String doubleStarName) { - final PyParameterList paramList = superInit.getParameterList(); - PyParameter[] parameters = paramList.getParameters(); - boolean addDouble = false; - boolean addStar = false; - for (int i = 1; i != parameters.length; i++) { - PyParameter p = parameters[i]; - if (p.getDefaultValue() != null) continue; - final String param = p.getName(); - String paramText = p.getText(); - if (paramText.startsWith("**")) { - addDouble = true; - if (doubleStarName == null) - doubleStarName = p.getText(); - continue; + private static class ParametersInfo { + + private final PyParameter mySelfParam; + /** + * Parameters without default value that come before first "*..." parameter. + */ + private final List myRequiredParams = new ArrayList(); + /** + * Parameters with default value that come before first "*..." parameter. + */ + private final List myOptionalParams = new ArrayList(); + /** + * Parameter of form "*args" (positional vararg), not the same as single "*". + */ + private final PyParameter myPositionalContainerParam; + /** + * Parameter "*", that is used to delimit normal and keyword-only parameters. + */ + private final PyParameter mySingleStarParam; + /** + * Parameters without default value that come after first "*..." parameter. + */ + private final List myRequiredKwOnlyParams = new ArrayList(); + /** + * Parameters with default value that come after first "*..." parameter. + */ + private final List myOptionalKwOnlyParams = new ArrayList(); + /** + * Parameter of form "**kwargs" (keyword vararg). + */ + private final PyParameter myKeywordContainerParam; + + private final Set myAllParameterNames = new LinkedHashSet(); + + public ParametersInfo(@NotNull PyParameterList parameterList) { + PyParameter positionalContainer = null; + PyParameter singleStarParam = null; + PyParameter keywordContainer = null; + PyParameter selfParam = null; + + for (PyParameter param : parameterList.getParameters()) { + myAllParameterNames.addAll(collectParameterNames(param)); + + if (param.isSelf()) { + selfParam = param; + } + else if (param.getText().equals("*")) { + singleStarParam = param; + } + else if (param.getText().startsWith("**")) { + keywordContainer = param; + } + else if (param.getText().startsWith("*")) { + positionalContainer = param; + } + else if (positionalContainer == null && singleStarParam == null) { + if (param.hasDefaultValue()) { + myOptionalParams.add(param); + } + else { + myRequiredParams.add(param); + } + } + else { + if (param.hasDefaultValue()) { + myOptionalKwOnlyParams.add(param); + } + else { + myRequiredKwOnlyParams.add(param); + } + } } - if (paramText.startsWith("*")) { - addStar = true; - if (starName == null) - starName = p.getText(); - continue; - } - if (addComma) - superCall.append(","); - superCall.append(param); - if (!functionParams.contains(param)) - newFunction.append(",").append(param); - addComma = true; + + mySelfParam = selfParam; + myPositionalContainerParam = positionalContainer; + mySingleStarParam = singleStarParam; + myKeywordContainerParam = keywordContainer; } - for(String p : problemParams) - newFunction.append(",").append(p); - if (starName != null) { - newFunction.append(",").append(starName); - if (addStar) { - if (addComma) superCall.append(","); - superCall.append(starName); - addComma = true; + + @Nullable + public PyParameter getSelfParameter() { + return mySelfParam; + } + + @NotNull + public List getRequiredParameters() { + return Collections.unmodifiableList(myRequiredParams); + } + + @NotNull + public List getOptionalParameters() { + return Collections.unmodifiableList(myOptionalParams); + } + + @Nullable + public PyParameter getPositionalContainerParameter() { + return myPositionalContainerParam; + } + + @Nullable + public PyParameter getSingleStarParameter() { + return mySingleStarParam; + } + + @NotNull + public List getRequiredKeywordOnlyParameters() { + return Collections.unmodifiableList(myRequiredKwOnlyParams); + } + + @NotNull + public List getOptionalKeywordOnlyParameters() { + return Collections.unmodifiableList(myOptionalKwOnlyParams); + } + + @Nullable + public PyParameter getKeywordContainerParameter() { + return myKeywordContainerParam; + } + + @NotNull + public Set getAllParameterNames() { + return Collections.unmodifiableSet(myAllParameterNames); + } + } + + @NotNull + private static List collectParameterNames(@NotNull PyParameter param) { + final List result = new ArrayList(); + collectParameterNames(param, result); + return result; + } + + + private static void collectParameterNames(@NotNull PyParameter param, @NotNull Collection acc) { + final PyTupleParameter tupleParam = param.getAsTuple(); + if (tupleParam != null) { + for (PyParameter subParam : tupleParam.getContents()) { + collectParameterNames(subParam, acc); } } - if (doubleStarName != null) { - newFunction.append(",").append(doubleStarName); - if (addDouble) { - if (addComma) superCall.append(","); - superCall.append(doubleStarName); - } + else { + ContainerUtil.addIfNotNull(acc, param.getName()); } } } diff --git a/python/testData/inspections/AddCallSuperAnnotations.py b/python/testData/inspections/AddCallSuperAnnotations.py deleted file mode 100644 index b50ae1b4fb72..000000000000 --- a/python/testData/inspections/AddCallSuperAnnotations.py +++ /dev/null @@ -1,8 +0,0 @@ -class Example1: - def __init__(self, field1: str): - self.field1 = field1 - - -class Example2(Example1): - def __init__(self): ## Missed call to __init__ of super class - pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperAnnotations_after.py b/python/testData/inspections/AddCallSuperAnnotations_after.py deleted file mode 100644 index 4bfd08c375e3..000000000000 --- a/python/testData/inspections/AddCallSuperAnnotations_after.py +++ /dev/null @@ -1,8 +0,0 @@ -class Example1: - def __init__(self, field1: str): - self.field1 = field1 - - -class Example2(Example1): - def __init__(self, field1): - super().__init__(field1) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperConflictingTupleParam.py b/python/testData/inspections/AddCallSuperConflictingTupleParam.py new file mode 100644 index 000000000000..276e16dc2e73 --- /dev/null +++ b/python/testData/inspections/AddCallSuperConflictingTupleParam.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, (a, (b, c)), (d, e)): + pass + +class B(A): + def __init__(self, (a, b), c, e): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperConflictingTupleParam_after.py b/python/testData/inspections/AddCallSuperConflictingTupleParam_after.py new file mode 100644 index 000000000000..5a41d4a81d09 --- /dev/null +++ b/python/testData/inspections/AddCallSuperConflictingTupleParam_after.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, (a, (b, c)), (d, e)): + pass + +class B(A): + def __init__(self, (a, b), c, e, d): + A.__init__(self, (a, (b, c)), (d, e)) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit.py b/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit.py new file mode 100644 index 000000000000..e1da8962d217 --- /dev/null +++ b/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, a): + pass + +class B(A): + def __init__(self, b, c=1, *args, kw_only): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit_after.py b/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit_after.py new file mode 100644 index 000000000000..925cac6746a3 --- /dev/null +++ b/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit_after.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, a): + pass + +class B(A): + def __init__(self, b, a, c=1, *args, kw_only): + super().__init__(a) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit.py b/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit.py new file mode 100644 index 000000000000..5248dd92742a --- /dev/null +++ b/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit.py @@ -0,0 +1,8 @@ +class A: + def __init__(self, a, b=1, *args, kw_only): + pass + + +class B(A): + def __init__(self, c): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit_after.py b/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit_after.py new file mode 100644 index 000000000000..0ee340e47d36 --- /dev/null +++ b/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit_after.py @@ -0,0 +1,8 @@ +class A: + def __init__(self, a, b=1, *args, kw_only): + pass + + +class B(A): + def __init__(self, c, a, *args, kw_only): + super().__init__(a, *args, kw_only=kw_only) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision.py b/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision.py new file mode 100644 index 000000000000..3d3bb7d4b64e --- /dev/null +++ b/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision.py @@ -0,0 +1,8 @@ +class A: + def __init__(self, a): + pass + + +class B(A): + def __init__(self, a=1): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision_after.py b/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision_after.py new file mode 100644 index 000000000000..1693904e6b58 --- /dev/null +++ b/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision_after.py @@ -0,0 +1,8 @@ +class A: + def __init__(self, a): + pass + + +class B(A): + def __init__(self, a=1): + A.__init__(self, a) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit.py b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit.py new file mode 100644 index 000000000000..7ff489252191 --- /dev/null +++ b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, *, kw_only, optional_kw_only=None): + pass + +class B(A): + def __init__(self): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit.py b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit.py new file mode 100644 index 000000000000..afca366ff1e3 --- /dev/null +++ b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, *, kw_only): + pass + +class B(A): + def __init__(self, *args, another_kw_only): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit_after.py b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit_after.py new file mode 100644 index 000000000000..01cf279b21fc --- /dev/null +++ b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit_after.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, *, kw_only): + pass + +class B(A): + def __init__(self, *args, another_kw_only, kw_only): + super().__init__(kw_only=kw_only) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit_after.py b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit_after.py new file mode 100644 index 000000000000..1eb8c4228300 --- /dev/null +++ b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit_after.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, *, kw_only, optional_kw_only=None): + pass + +class B(A): + def __init__(self, *, kw_only): + super().__init__(kw_only=kw_only) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved.py b/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved.py new file mode 100644 index 000000000000..76b33e20063e --- /dev/null +++ b/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, a:int, b:float, *args:tuple, c:complex, **kwargs:dict) -> None: + pass + +class B(A): + def __init__(self, d:str, *, e:bytes) -> list: + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved_after.py b/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved_after.py new file mode 100644 index 000000000000..c8b5b3e67c00 --- /dev/null +++ b/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved_after.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, a:int, b:float, *args:tuple, c:complex, **kwargs:dict) -> None: + pass + +class B(A): + def __init__(self, d:str, a:int, b:float, *args:tuple, e:bytes, c:complex, **kwargs:dict) -> list: + super().__init__(a, b, *args, c=c, **kwargs) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java b/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java index f757da9f3fac..10ed5fa597b4 100644 --- a/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java @@ -24,6 +24,7 @@ import com.jetbrains.python.inspections.PyMissingConstructorInspection; import com.jetbrains.python.inspections.PyStatementEffectInspection; import com.jetbrains.python.psi.LanguageLevel; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; @TestDataPath("$CONTENT_ROOT/../testData/inspections/") public class Py3QuickFixTest extends PyTestCase { @@ -32,39 +33,107 @@ public class Py3QuickFixTest extends PyTestCase { return PyTestCase.ourPy3Descriptor; } + // PY-13685 public void testReplacePrintEnd() { runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() { @Override public void run() { - doInspectionTest("ReplacePrintEnd.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); - }}); + doInspectionTest(PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); + } + }); } + // PY-13685 public void testReplacePrintComment() { runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() { @Override public void run() { - doInspectionTest("ReplacePrintComment.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, - true); - }}); + doInspectionTest(PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); + } + }); } + // PY-13685 public void testReplaceExecComment() { runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() { @Override public void run() { - doInspectionTest("ReplaceExecComment.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, - true); - }}); + doInspectionTest(PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); + } + }); } - public void testAddCallSuperAnnotations() { - runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() { + // PY-15867 + public void testAddCallSuperKeywordOnlyParamInSuperInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + public void run() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-15867 + public void testAddCallSuperKeywordOnlyParamInInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + public void run() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-15867 + public void testAddCallSuperSingleStarParamInSuperInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + public void run() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-15867 + public void testAddCallSuperSingleStarParamInSuperInitAndVarargInInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { @Override public void run() { - doInspectionTest("AddCallSuperAnnotations.py", - PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-11561 + public void testAddCallSuperTypeAnnotationsPreserved() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + @Override + public void run() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-8991 + public void testRemoveUnicodePrefixFromGluedStringNodesWithSlash() { + runWithLanguageLevel(LanguageLevel.PYTHON32, new Runnable() { + public void run() { + myFixture.configureByFile(getTestName(false) + ".py"); + myFixture.checkHighlighting(true, false, false); + final IntentionAction intentionAction = myFixture.findSingleIntention(PyBundle.message("INTN.remove.leading.$0", "U")); + assertNotNull(intentionAction); + myFixture.launchAction(intentionAction); + myFixture.checkResultByFile(getTestName(false) + "_after.py"); + } + }); + } + + // PY-8990 + public void testRemoveUnicodePrefixFromGluedStringNodesInParenthesis() { + runWithLanguageLevel(LanguageLevel.PYTHON32, new Runnable() { + public void run() { + myFixture.configureByFile(getTestName(false) + ".py"); + myFixture.checkHighlighting(true, false, false); + final IntentionAction intentionAction = myFixture.findSingleIntention(PyBundle.message("INTN.remove.leading.$0", "U")); + assertNotNull(intentionAction); + myFixture.launchAction(intentionAction); + myFixture.checkResultByFile(getTestName(false) + "_after.py"); } }); } @@ -75,9 +144,16 @@ public class Py3QuickFixTest extends PyTestCase { return PythonTestUtil.getTestDataPath() + "/inspections/"; } - protected void doInspectionTest(@TestDataFile @NonNls String testFileName, - final Class inspectionClass, - @NonNls String quickFixName, + private void doInspectionTest(@NotNull Class inspectionClass, + @NotNull String quickFixName, + boolean applyFix, + boolean available) { + doInspectionTest(getTestName(false) + ".py", inspectionClass, quickFixName, applyFix, available); + } + + protected void doInspectionTest(@TestDataFile @NonNls @NotNull String testFileName, + @NotNull Class inspectionClass, + @NonNls @NotNull String quickFixName, boolean applyFix, boolean available) { doInspectionTest(new String[]{testFileName}, inspectionClass, quickFixName, applyFix, available); @@ -93,9 +169,9 @@ public class Py3QuickFixTest extends PyTestCase { * @param available true if the fix should be available, false if it should be explicitly not available. * @throws Exception */ - protected void doInspectionTest(@NonNls String[] testFiles, - final Class inspectionClass, - @NonNls String quickFixName, + protected void doInspectionTest(@NonNls @NotNull String[] testFiles, + @NotNull Class inspectionClass, + @NonNls @NotNull String quickFixName, boolean applyFix, boolean available) { myFixture.enableInspections(inspectionClass); diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 9a55254d8287..1fc8572ae262 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -27,6 +27,7 @@ import com.jetbrains.python.inspections.*; import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection; import com.jetbrains.python.psi.LanguageLevel; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; /** * @author dcheryasov @@ -47,15 +48,19 @@ public class PyQuickFixTest extends PyTestCase { } public void testAddImport() { - doInspectionTest(new String[] { "AddImport.py", "ImportTarget.py" }, PyUnresolvedReferencesInspection.class, "Import 'ImportTarget'", true, true); + doInspectionTest(new String[]{"AddImport.py", "ImportTarget.py"}, PyUnresolvedReferencesInspection.class, + "Import 'ImportTarget'", true, true); } public void testAddImportDoc() { - doInspectionTest(new String[] { "AddImportDoc.py", "ImportTarget.py" }, PyUnresolvedReferencesInspection.class, "Import 'ImportTarget'", true, true); + doInspectionTest(new String[]{"AddImportDoc.py", "ImportTarget.py"}, PyUnresolvedReferencesInspection.class, + "Import 'ImportTarget'", true, true); } - public void testAddImportDocComment() { // PY-728 - doInspectionTest(new String[] { "AddImportDocComment.py", "ImportTarget.py" }, PyUnresolvedReferencesInspection.class, "Import 'ImportTarget'", true, true); + // PY-728 + public void testAddImportDocComment() { + doInspectionTest(new String[]{"AddImportDocComment.py", "ImportTarget.py"}, PyUnresolvedReferencesInspection.class, + "Import 'ImportTarget'", true, true); } public void testImportFromModule() { @@ -73,7 +78,8 @@ public class PyQuickFixTest extends PyTestCase { assertEquals("Import 'module.MyOldStyleClass'", intention.getText()); } - public void testImportFromModuleStar() { // PY-6302 + // PY-6302 + public void testImportFromModuleStar() { myFixture.enableInspections(PyUnresolvedReferencesInspection.class); myFixture.copyDirectoryToProject("importFromModuleStar", ""); myFixture.configureFromTempProjectFile("source.py"); @@ -91,7 +97,8 @@ public class PyQuickFixTest extends PyTestCase { settings.PREFER_FROM_IMPORT = false; settings.HIGHLIGHT_UNUSED_IMPORTS = false; try { - doInspectionTest(new String[]{"QualifyByImport.py", "QualifyByImportFoo.py"}, PyUnresolvedReferencesInspection.class, PyBundle.message("ACT.qualify.with.module"), true, true); + doInspectionTest(new String[]{"QualifyByImport.py", "QualifyByImportFoo.py"}, PyUnresolvedReferencesInspection.class, + PyBundle.message("ACT.qualify.with.module"), true, true); } finally { settings.PREFER_FROM_IMPORT = oldPreferFrom; @@ -104,7 +111,8 @@ public class PyQuickFixTest extends PyTestCase { boolean oldHighlightUnused = settings.HIGHLIGHT_UNUSED_IMPORTS; settings.HIGHLIGHT_UNUSED_IMPORTS = false; try { - doInspectionTest(new String[]{"AddToImportFromList.py", "AddToImportFromFoo.py"}, PyUnresolvedReferencesInspection.class, "Import 'foo(a) from AddToImportFromFoo'", true, true); + doInspectionTest(new String[]{"AddToImportFromList.py", "AddToImportFromFoo.py"}, PyUnresolvedReferencesInspection.class, + "Import 'foo(a) from AddToImportFromFoo'", true, true); } finally { settings.HIGHLIGHT_UNUSED_IMPORTS = oldHighlightUnused; @@ -115,90 +123,86 @@ public class PyQuickFixTest extends PyTestCase { // TODO: add tests for stub indexes-based autoimport of unimported somehow. public void testAddSelf() { - doInspectionTest("AddSelf.py", PyMethodParametersInspection.class, PyBundle.message("QFIX.add.parameter.self", "self"), true, true); + doInspectionTest(PyMethodParametersInspection.class, PyBundle.message("QFIX.add.parameter.self", "self"), true, true); } public void testReplacePrint() { - doInspectionTest("ReplacePrint.py", PyCompatibilityInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); + doInspectionTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); } - public void testAddSelfFunction() { //PY-4556 - doInspectionTest("AddSelfFunction.py", - PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference", "get_a", "self"), true, true); + // PY-4556 + public void testAddSelfFunction() { + doInspectionTest("AddSelfFunction.py", PyUnresolvedReferencesInspection.class, + PyBundle.message("QFIX.unresolved.reference", "get_a", "self"), true, true); } - public void testAddSelfToClassmethod() { //PY-9721 - doInspectionTest("AddSelfToClassmethod.py", - PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference", "foo", "cls"), true, true); + // PY-9721 + public void testAddSelfToClassmethod() { + doInspectionTest("AddSelfToClassmethod.py", PyUnresolvedReferencesInspection.class, + PyBundle.message("QFIX.unresolved.reference", "foo", "cls"), true, true); } public void testAddCls() { - doInspectionTest("AddCls.py", PyMethodParametersInspection.class, PyBundle.message("QFIX.add.parameter.self", "cls"), true, true); + doInspectionTest(PyMethodParametersInspection.class, PyBundle.message("QFIX.add.parameter.self", "cls"), true, true); } public void testRenameToSelf() { - doInspectionTest("RenameToSelf.py", PyMethodParametersInspection.class, PyBundle.message("QFIX.rename.parameter.to.$0", "self"), true, - true); + doInspectionTest(PyMethodParametersInspection.class, PyBundle.message("QFIX.rename.parameter.to.$0", "self"), true, true); } public void testRemoveTrailingSemicolon() { - doInspectionTest("RemoveTrailingSemicolon.py", PyTrailingSemicolonInspection.class, PyBundle.message("QFIX.remove.trailing.semicolon"), - true, true); + doInspectionTest(PyTrailingSemicolonInspection.class, PyBundle.message("QFIX.remove.trailing.semicolon"), true, true); } public void testDictCreation() { - doInspectionTest("DictCreation.py", PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); + doInspectionTest(PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); } - public void testDictCreationTuple() { //PY-6283 - doInspectionTest("DictCreationTuple.py", PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); + // PY-6283 + public void testDictCreationTuple() { + doInspectionTest(PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); } - public void testDictCreationDuplicate() { //PY-7318 - doInspectionTest("DictCreationDuplicate.py", PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); + // PY-7318 + public void testDictCreationDuplicate() { + doInspectionTest(PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); } public void testTransformClassicClass() { - doInspectionTest("TransformClassicClass.py", PyClassicStyleClassInspection.class, - PyBundle.message("QFIX.classic.class.transform"), true, true); + doInspectionTest(PyClassicStyleClassInspection.class, PyBundle.message("QFIX.classic.class.transform"), true, true); } - public void testAddGlobalQuickFix() { - doInspectionTest("AddGlobalStatement.py", PyUnboundLocalVariableInspection.class, - PyBundle.message("QFIX.add.global"), true, true); + public void testAddGlobalStatement() { + doInspectionTest(PyUnboundLocalVariableInspection.class, PyBundle.message("QFIX.add.global"), true, true); } - public void testAddGlobalExistingQuickFix() { - doInspectionTest("AddGlobalExistingStatement.py", PyUnboundLocalVariableInspection.class, - PyBundle.message("QFIX.add.global"), true, true); + public void testAddGlobalExistingStatement() { + doInspectionTest(PyUnboundLocalVariableInspection.class, PyBundle.message("QFIX.add.global"), true, true); } - public void testSimplifyBooleanCheckQuickFix() { - doInspectionTest("SimplifyBooleanCheck.py", PySimplifyBooleanCheckInspection.class, - PyBundle.message("QFIX.simplify.$0", "b"), true, true); + public void testSimplifyBooleanCheck() { + doInspectionTest(PySimplifyBooleanCheckInspection.class, PyBundle.message("QFIX.simplify.$0", "b"), true, true); } - public void testFromFutureImportQuickFix() { - doInspectionTest("MoveFromFutureImport.py", PyFromFutureImportInspection.class, - PyBundle.message("QFIX.move.from.future.import"), true, true); + public void testMoveFromFutureImport() { + doInspectionTest(PyFromFutureImportInspection.class, PyBundle.message("QFIX.move.from.future.import"), true, true); } - public void testFromFutureImportQuickFixDocString() { // PY-10080 - doInspectionTest("MoveFromFutureImportDocString.py", PyFromFutureImportInspection.class, - PyBundle.message("QFIX.move.from.future.import"), true, true); + // PY-10080 + public void testMoveFromFutureImportDocString() { + doInspectionTest(PyFromFutureImportInspection.class, PyBundle.message("QFIX.move.from.future.import"), true, true); } - public void testComparisonWithNoneQuickFix() { - doInspectionTest("ComparisonWithNone.py", PyComparisonWithNoneInspection.class, - PyBundle.message("QFIX.replace.equality"), true, true); + public void testComparisonWithNone() { + doInspectionTest(PyComparisonWithNoneInspection.class, PyBundle.message("QFIX.replace.equality"), true, true); } public void testAddClassFix() { doInspectionTest("AddClass.py", PyUnresolvedReferencesInspection.class, "Create class 'Xyzzy'", true, true); } - - public void testAddFunctionToModule() { // PY-1602 + // PY-1602 + public void testAddFunctionToModule() { doInspectionTest( "AddFunctionToModule.py", PyUnresolvedReferencesInspection.class, @@ -207,7 +211,8 @@ public class PyQuickFixTest extends PyTestCase { ); } - public void testRedundantParentheses() { // PY-1470 + // PY-1470 + public void testRedundantParentheses() { String[] testFiles = new String[]{"RedundantParentheses.py"}; myFixture.enableInspections(PyRedundantParenthesesInspection.class); myFixture.configureByFiles(testFiles); @@ -216,88 +221,86 @@ public class PyQuickFixTest extends PyTestCase { assertNotNull(intentionAction); myFixture.launchAction(intentionAction); myFixture.checkResultByFile(graftBeforeExt(testFiles[0], "_after")); - } - public void testRedundantParenthesesBoolean() { // PY-3095 - doInspectionTest("RedundantParenthesesBoolean.py", PyRedundantParenthesesInspection.class, - PyBundle.message("QFIX.redundant.parentheses"), true, true); + // PY-3095 + public void testRedundantParenthesesBoolean() { + doInspectionTest(PyRedundantParenthesesInspection.class, PyBundle.message("QFIX.redundant.parentheses"), true, true); } - public void testRedundantParenthesesMore() { // PY-3239 - doInspectionTest("RedundantParenthesesMore.py", PyRedundantParenthesesInspection.class, - PyBundle.message("QFIX.redundant.parentheses"), true, true); + // PY-3239 + public void testRedundantParenthesesMore() { + doInspectionTest(PyRedundantParenthesesInspection.class, PyBundle.message("QFIX.redundant.parentheses"), true, true); } - public void testRedundantParenthesesParenthesizedExpression() { // PY-12679 - doInspectionTest("RedundantParenthesesParenthesizedExpression.py", PyRedundantParenthesesInspection.class, - PyBundle.message("QFIX.redundant.parentheses"), true, true); + // PY-12679 + public void testRedundantParenthesesParenthesizedExpression() { + doInspectionTest(PyRedundantParenthesesInspection.class, PyBundle.message("QFIX.redundant.parentheses"), true, true); } // PY-15506 public void testEmptyListOfBaseClasses() { - doInspectionTest("EmptyListOfBaseClasses.py", PyRedundantParenthesesInspection.class, - PyBundle.message("QFIX.redundant.parentheses"), true, true); + doInspectionTest(PyRedundantParenthesesInspection.class, PyBundle.message("QFIX.redundant.parentheses"), true, true); } - public void testChainedComparisons() { // PY-1020 - doInspectionTest("ChainedComparisons.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-1020 + public void testChainedComparisons() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testChainedComparison1() { // PY-3126 - doInspectionTest("ChainedComparison1.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-3126 + public void testChainedComparison1() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testChainedComparison2() { // PY-3126 - doInspectionTest("ChainedComparison2.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-3126 + public void testChainedComparison2() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testChainedComparison3() { // PY-3126 - doInspectionTest("ChainedComparison3.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-3126 + public void testChainedComparison3() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testChainedComparison4() { // PY-5623 - doInspectionTest("ChainedComparison4.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-5623 + public void testChainedComparison4() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testChainedComparison5() { // PY-6467 - doInspectionTest("ChainedComparison5.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-6467 + public void testChainedComparison5() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } // PY-14002 public void testChainedComparisonWithCommonBinaryExpression() { - doInspectionTest("ChainedComparisonWithCommonBinaryExpression.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testStatementEffect() { // PY-1362, PY-2585 - doInspectionTest("StatementEffect.py", PyStatementEffectInspection.class, - PyBundle.message("QFIX.statement.effect"), true, true); + // PY-1362, PY-2585 + public void testStatementEffect() { + doInspectionTest(PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); } - public void testStatementEffectIntroduceVariable() { // PY-1265 - doInspectionTest("StatementEffectIntroduceVariable.py", PyStatementEffectInspection.class, - PyBundle.message("QFIX.statement.effect.introduce.variable"), true, true); + // PY-1265 + public void testStatementEffectIntroduceVariable() { + doInspectionTest(PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect.introduce.variable"), true, true); } - public void testUnresolvedWith() { // PY-2083 + // PY-2083 + public void testUnresolvedWith() { runWithLanguageLevel(LanguageLevel.PYTHON25, new Runnable() { @Override public void run() { - doInspectionTest("UnresolvedWith.py", PyUnresolvedReferencesInspection.class, - PyBundle.message("QFIX.unresolved.reference.add.future"), true, true); + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference.add.future"), true, true); } }); } - public void testUnresolvedRefCreateFunction() { // PY-2092 - doInspectionTest("UnresolvedRefCreateFunction.py", PyUnresolvedReferencesInspection.class, + // PY-2092 + public void testUnresolvedRefCreateFunction() { + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference.create.function.$0", "ref"), true, true); } @@ -305,109 +308,113 @@ public class PyQuickFixTest extends PyTestCase { myFixture.enableInspections(PyUnresolvedReferencesInspection.class); myFixture.configureByFile("UnresolvedRefNoCreateFunction.py"); myFixture.checkHighlighting(true, false, false); - final IntentionAction intentionAction = myFixture.getAvailableIntention( - PyBundle.message("QFIX.unresolved.reference.create.function.$0", "ref")); + final IntentionAction intentionAction = myFixture.getAvailableIntention(PyBundle.message("QFIX.unresolved.reference.create.function.$0", "ref")); assertNull(intentionAction); } public void testReplaceNotEqOperator() { - doInspectionTest("ReplaceNotEqOperator.py", PyCompatibilityInspection.class, - PyBundle.message("INTN.replace.noteq.operator"), true, true); + doInspectionTest(PyCompatibilityInspection.class, PyBundle.message("INTN.replace.noteq.operator"), true, true); } public void testListCreation() { - doInspectionTest("ListCreation.py", PyListCreationInspection.class, - PyBundle.message("QFIX.list.creation"), true, true); + doInspectionTest(PyListCreationInspection.class, PyBundle.message("QFIX.list.creation"), true, true); } - public void testConvertSingleQuotedDocstring() { //PY-1445 - doInspectionTest("ConvertSingleQuotedDocstring.py", PySingleQuotedDocstringInspection.class, - PyBundle.message("QFIX.convert.single.quoted.docstring"), true, true); + // PY-1445 + public void testConvertSingleQuotedDocstring() { + doInspectionTest(PySingleQuotedDocstringInspection.class, PyBundle.message("QFIX.convert.single.quoted.docstring"), true, true); } - public void testConvertSingleQuotedDocstringEscape() { //PY-8926 - doInspectionTest("ConvertSingleQuotedDocstringEscape.py", PySingleQuotedDocstringInspection.class, - PyBundle.message("QFIX.convert.single.quoted.docstring"), true, true); + // PY-8926 + public void testConvertSingleQuotedDocstringEscape() { + doInspectionTest(PySingleQuotedDocstringInspection.class, PyBundle.message("QFIX.convert.single.quoted.docstring"), true, true); } - public void testDefaultArgument() { //PY-3127 - doInspectionTest("DefaultArgument.py", PyDefaultArgumentInspection.class, - PyBundle.message("QFIX.default.argument"), true, true); + // PY-3127 + public void testDefaultArgument() { + doInspectionTest(PyDefaultArgumentInspection.class, PyBundle.message("QFIX.default.argument"), true, true); } public void testDefaultArgumentEmptyList() { - doInspectionTest("DefaultArgumentEmptyList.py", PyDefaultArgumentInspection.class, - PyBundle.message("QFIX.default.argument"), true, true); + doInspectionTest(PyDefaultArgumentInspection.class, PyBundle.message("QFIX.default.argument"), true, true); } - public void testPyArgumentEqualDefault() { //PY-3125 - doInspectionTest("ArgumentEqualDefault.py", PyArgumentEqualDefaultInspection.class, - PyBundle.message("QFIX.remove.argument.equal.default"), true, true); + // PY-3125 + public void testArgumentEqualDefault() { + doInspectionTest(PyArgumentEqualDefaultInspection.class, PyBundle.message("QFIX.remove.argument.equal.default"), true, true); } - public void testAddCallSuper() { //PY-3315 - doInspectionTest("AddCallSuper.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + // PY-3315 + public void testAddCallSuper() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } - public void testAddCallSuper1() { //PY-4017 - doInspectionTest("AddCallSuper1.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + // PY-4017 + public void testAddCallSuper1() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } - public void testAddCallSuperPass() { //PY-8654 - doInspectionTest("AddCallSuperPass.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + // PY-8654 + public void testAddCallSuperPass() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } - //PY-491, PY-13297 + // PY-15867 + public void testAddCallSuperOptionalAndRequiredParamsNameCollision() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + + // PY-15927 + public void testAddCallSuperConflictingTupleParam() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + + // PY-491, PY-13297 public void testAddEncoding() { - doInspectionTest("AddEncoding.py", PyMandatoryEncodingInspection.class, - PyBundle.message("QFIX.add.encoding"), true, true); + doInspectionTest(PyMandatoryEncodingInspection.class, PyBundle.message("QFIX.add.encoding"), true, true); } // PY-13297 public void testAddEncodingAtLastLine() { - doInspectionTest("AddEncodingAtLastLine.py", PyMandatoryEncodingInspection.class, - PyBundle.message("QFIX.add.encoding"), true, true); + doInspectionTest(PyMandatoryEncodingInspection.class, PyBundle.message("QFIX.add.encoding"), true, true); } - public void testRemoveDecorator() { //PY-3348 - doInspectionTest("RemoveDecorator.py", PyDecoratorInspection.class, - PyBundle.message("QFIX.remove.decorator"), true, true); + // PY-3348 + public void testRemoveDecorator() { + doInspectionTest(PyDecoratorInspection.class, PyBundle.message("QFIX.remove.decorator"), true, true); } public void testAddParameter() { - doInspectionTest("AddParameter.py", PyUnresolvedReferencesInspection.class, + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference.add.param.$0", "test"), true, true); } - public void testRenameUnresolvedReference() { //PY-6595 - doInspectionTest("RenameUnresolvedReference.py", PyUnresolvedReferencesInspection.class, - PyBundle.message("QFIX.rename.unresolved.reference"), true, true); + // PY-6595 + public void testRenameUnresolvedReference() { + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.rename.unresolved.reference"), true, true); } - public void testSetFunctionToLiteral() { //PY-3120 + // PY-3120 + public void testSetFunctionToLiteral() { runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() { @Override public void run() { - doInspectionTest("SetFunctionToLiteral.py", PySetFunctionToLiteralInspection.class, - PyBundle.message("QFIX.replace.function.set.with.literal"), true, true); + doInspectionTest(PySetFunctionToLiteralInspection.class, PyBundle.message("QFIX.replace.function.set.with.literal"), true, true); } }); } public void testDictComprehensionToCall() { - doInspectionTest("DictComprehensionToCall.py", PyCompatibilityInspection.class, - PyBundle.message("INTN.convert.dict.comp.to"), true, true); + doInspectionTest(PyCompatibilityInspection.class, PyBundle.message("INTN.convert.dict.comp.to"), true, true); } - public void testDocstringParams() { //PY-3394 + // PY-3394 + public void testDocstringParams() { PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(myFixture.getModule()); documentationSettings.setFormat(DocStringFormat.EPYTEXT); try { - doInspectionTest("DocstringParams.py", PyDocstringInspection.class, - PyBundle.message("QFIX.docstring.add.$0", "b"), true, true); + doInspectionTest(PyDocstringInspection.class, + PyBundle.message("QFIX.docstring.add.$0", "b"), true, true); } finally { documentationSettings.setFormat(DocStringFormat.PLAIN); @@ -418,20 +425,19 @@ public class PyQuickFixTest extends PyTestCase { PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(myFixture.getModule()); documentationSettings.setFormat(DocStringFormat.EPYTEXT); try { - doInspectionTest("DocstringParams1.py", PyDocstringInspection.class, - PyBundle.message("QFIX.docstring.remove.$0", "c"), true, true); + doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "c"), true, true); } finally { documentationSettings.setFormat(DocStringFormat.PLAIN); } } - public void testDocstringParams2() { //PY-4964 + // PY-4964 + public void testDocstringParams2() { PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(myFixture.getModule()); documentationSettings.setFormat(DocStringFormat.EPYTEXT); try { - doInspectionTest("DocstringParams2.py", PyDocstringInspection.class, - PyBundle.message("QFIX.docstring.add.$0", "ham"), true, true); + doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "ham"), true, true); } finally { documentationSettings.setFormat(DocStringFormat.PLAIN); @@ -449,9 +455,9 @@ public class PyQuickFixTest extends PyTestCase { myFixture.checkResultByFile(graftBeforeExt(testFiles[0], "_after")); } - public void testUnresolvedRefTrueFalse() { //PY-3051 - doInspectionTest("UnresolvedRefTrueFalse.py", PyUnresolvedReferencesInspection.class, - PyBundle.message("QFIX.unresolved.reference.replace.$0", "True"), true, true); + // PY-3051 + public void testUnresolvedRefTrueFalse() { + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference.replace.$0", "True"), true, true); } public void testUnnecessaryBackslashInArgumentList() { @@ -498,43 +504,22 @@ public class PyQuickFixTest extends PyTestCase { myFixture.checkHighlighting(true, false, true); } - // PY-8991 - public void testRemoveUnicodePrefixFromGluedStringNodesWithSlash() { - runWithLanguageLevel(LanguageLevel.PYTHON32, new Runnable() { - public void run() { - myFixture.configureByFile(getTestName(false) + ".py"); - myFixture.checkHighlighting(true, false, false); - final IntentionAction intentionAction = myFixture.findSingleIntention(PyBundle.message("INTN.remove.leading.$0", "U")); - assertNotNull(intentionAction); - myFixture.launchAction(intentionAction); - myFixture.checkResultByFile(getTestName(false) + "_after.py"); - } - }); - } - - // PY-8990 - public void testRemoveUnicodePrefixFromGluedStringNodesInParenthesis() { - runWithLanguageLevel(LanguageLevel.PYTHON32, new Runnable() { - public void run() { - myFixture.configureByFile(getTestName(false) + ".py"); - myFixture.checkHighlighting(true, false, false); - final IntentionAction intentionAction = myFixture.findSingleIntention(PyBundle.message("INTN.remove.leading.$0", "U")); - assertNotNull(intentionAction); - myFixture.launchAction(intentionAction); - myFixture.checkResultByFile(getTestName(false) + "_after.py"); - } - }); - } - @Override @NonNls protected String getTestDataPath() { return PythonTestUtil.getTestDataPath() + "/inspections/"; } - protected void doInspectionTest(@TestDataFile @NonNls String testFileName, - final Class inspectionClass, - @NonNls String quickFixName, + private void doInspectionTest(@NotNull Class inspectionClass, + @NotNull String quickFixName, + boolean applyFix, + boolean available) { + doInspectionTest(getTestName(false) + ".py", inspectionClass, quickFixName, applyFix, available); + } + + protected void doInspectionTest(@TestDataFile @NonNls @NotNull String testFileName, + @NotNull Class inspectionClass, + @NonNls @NotNull String quickFixName, boolean applyFix, boolean available) { doInspectionTest(new String[]{testFileName}, inspectionClass, quickFixName, applyFix, available); @@ -550,9 +535,9 @@ public class PyQuickFixTest extends PyTestCase { * @param available true if the fix should be available, false if it should be explicitly not available. * @throws Exception */ - protected void doInspectionTest(@NonNls String[] testFiles, - final Class inspectionClass, - @NonNls String quickFixName, + protected void doInspectionTest(@NonNls @NotNull String[] testFiles, + @NotNull Class inspectionClass, + @NonNls @NotNull String quickFixName, boolean applyFix, boolean available) { myFixture.enableInspections(inspectionClass); diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index a518d3a5e771..2f1aa5b7bb32 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1621,6 +1621,7 @@ + emptyList() - : Collections.singleton(Urls.newFromVirtualFile(file)); + : Collections.singletonList(Urls.newFromVirtualFile(file)); } @NotNull