diff --git a/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpNamedGroupRefImpl.java b/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpNamedGroupRefImpl.java index 634e069bbbf0..f793abc807ab 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpNamedGroupRefImpl.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpNamedGroupRefImpl.java @@ -22,7 +22,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.tree.TokenSet; -import com.intellij.psi.util.PsiElementFilter; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import org.intellij.lang.regexp.RegExpTT; @@ -53,15 +52,12 @@ public class RegExpNamedGroupRefImpl extends RegExpElementImpl implements RegExp @Nullable public RegExpGroup resolve() { final PsiElementProcessor.FindFilteredElement processor = new PsiElementProcessor.FindFilteredElement<>( - new PsiElementFilter() { - @Override - public boolean isAccepted(PsiElement element) { - if (!(element instanceof RegExpGroup)) { - return false; - } - final RegExpGroup group = (RegExpGroup)element; - return group.isAnyNamedGroup() && Comparing.equal(getGroupName(), group.getGroupName()); + element -> { + if (!(element instanceof RegExpGroup)) { + return false; } + final RegExpGroup group = (RegExpGroup)element; + return group.isAnyNamedGroup() && Comparing.equal(getGroupName(), group.getGroupName()); } ); PsiTreeUtil.processElements(getContainingFile(), processor); @@ -142,16 +138,7 @@ public class RegExpNamedGroupRefImpl extends RegExpElementImpl implements RegExp @NotNull public Object[] getVariants() { final PsiElementProcessor.CollectFilteredElements processor = new PsiElementProcessor.CollectFilteredElements<>( - new PsiElementFilter() { - @Override - public boolean isAccepted(PsiElement element) { - if (!(element instanceof RegExpGroup)) { - return false; - } - final RegExpGroup regExpGroup = (RegExpGroup)element; - return regExpGroup.isAnyNamedGroup(); - } - } + e -> e instanceof RegExpGroup && ((RegExpGroup)e).isAnyNamedGroup() ); PsiTreeUtil.processElements(getContainingFile(), processor); return processor.toArray(); diff --git a/RegExpSupport/src/org/intellij/lang/regexp/validation/RegExpAnnotator.java b/RegExpSupport/src/org/intellij/lang/regexp/validation/RegExpAnnotator.java index c4f04f7213b5..aff8abcc2673 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/validation/RegExpAnnotator.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/validation/RegExpAnnotator.java @@ -20,7 +20,9 @@ import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.lang.ASTNode; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; +import com.intellij.lang.annotation.AnnotationSession; import com.intellij.lang.annotation.Annotator; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; @@ -35,7 +37,9 @@ import org.intellij.lang.regexp.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; public final class RegExpAnnotator extends RegExpElementVisitor implements Annotator { @@ -43,6 +47,7 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot "alnum", "alpha", "ascii", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "word", "xdigit"); private AnnotationHolder myHolder; private final RegExpLanguageHosts myLanguageHosts; + private final Key> NAMED_GROUP_MAP = new Key<>("REG_EXP_NAMED_GROUP_MAP"); public RegExpAnnotator() { myLanguageHosts = RegExpLanguageHosts.getInstance(); @@ -288,6 +293,13 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot final ASTNode node = group.getNode().findChildByType(RegExpTT.NAME); if (node != null) myHolder.createErrorAnnotation(node, "Invalid group name"); } + final AnnotationSession session = myHolder.getCurrentAnnotationSession(); + final Map namedGroups = NAMED_GROUP_MAP.get(session, new HashMap<>()); + if (namedGroups.isEmpty()) session.putUserData(NAMED_GROUP_MAP, namedGroups); + if (namedGroups.put(name, group) != null) { + final ASTNode node = group.getNode().findChildByType(RegExpTT.NAME); + if (node != null) myHolder.createErrorAnnotation(node, "Group with name '" + name + "' already defined"); + } final RegExpGroup.Type groupType = group.getType(); if (groupType == RegExpGroup.Type.POSITIVE_LOOKBEHIND || groupType == RegExpGroup.Type.NEGATIVE_LOOKBEHIND) { final RegExpLanguageHost.Lookbehind support = myLanguageHosts.supportsLookbehind(group); diff --git a/bin/scripts/unix/idea.sh b/bin/scripts/unix/idea.sh index 7df94672fb65..08811358451a 100755 --- a/bin/scripts/unix/idea.sh +++ b/bin/scripts/unix/idea.sh @@ -147,6 +147,17 @@ BITS=$? "$RM" -f "$VERSION_LOG" test ${BITS} -eq 0 && BITS="64" || BITS="" +#---------------------------------------------------------------------- +# Set platform enviroment variables for IDE +#---------------------------------------------------------------------- +if [ "$OS_TYPE" = "Linux" ] ; then + case "$XDG_CURRENT_DESKTOP" in + *Unity*) + export JAYATANA_FORCE=true + ;; + esac +fi + # --------------------------------------------------------------------- # Collect JVM options and IDE properties. # --------------------------------------------------------------------- diff --git a/bin/win/elevator.exe b/bin/win/elevator.exe index 1f04f3f3d480..8e04f0477a0b 100644 Binary files a/bin/win/elevator.exe and b/bin/win/elevator.exe differ diff --git a/build.txt b/build.txt index 38383cb00036..7bd408d4f89c 100644 --- a/build.txt +++ b/build.txt @@ -1 +1 @@ -172.SNAPSHOT +173.SNAPSHOT diff --git a/community-resources/src/idea/IdeaApplicationInfo.xml b/community-resources/src/idea/IdeaApplicationInfo.xml index 37c211591ec1..c032b6e579f4 100644 --- a/community-resources/src/idea/IdeaApplicationInfo.xml +++ b/community-resources/src/idea/IdeaApplicationInfo.xml @@ -1,7 +1,7 @@ - + diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java index 40cb2be97daa..b5fa6b017b1a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java @@ -92,7 +92,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV myContextSet = contextSet; } - static JavaValue create(JavaValue parent, + public static JavaValue create(JavaValue parent, @NotNull ValueDescriptorImpl valueDescriptor, @NotNull EvaluationContextImpl evaluationContext, NodeManagerImpl nodeManager, @@ -364,11 +364,6 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV return myValueDescriptor; } - @Override - public void setRemaining(int remaining) { - node.tooManyChildren(remaining); - } - @Override public void initChildrenArrayRenderer(ArrayRenderer renderer) { renderer.START_INDEX = myCurrentChildrenStart; @@ -408,6 +403,36 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV @Nullable XDebuggerTreeNodeHyperlink link) { node.setMessage(message, icon, attributes, link); } + + @Override + public void addChildren(@NotNull XValueChildrenList children, boolean last) { + node.addChildren(children, last); + } + + @Override + public void tooManyChildren(int remaining) { + node.tooManyChildren(remaining); + } + + @Override + public void setAlreadySorted(boolean alreadySorted) { + node.setAlreadySorted(alreadySorted); + } + + @Override + public void setErrorMessage(@NotNull String errorMessage) { + node.setErrorMessage(errorMessage); + } + + @Override + public void setErrorMessage(@NotNull String errorMessage, @Nullable XDebuggerTreeNodeHyperlink link) { + node.setErrorMessage(errorMessage, link); + } + + @Override + public boolean isObsolete() { + return node.isObsolete(); + } }, myEvaluationContext); } }); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java b/java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java new file mode 100644 index 000000000000..501962e2cf1d --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2017 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.debugger.ui; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.openapi.util.Key; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author egor + */ +public class OverheadTimings { + public static final Key KEY = Key.create("OVERHEAD_TIMINGS"); + + private final Map myMap = new ConcurrentHashMap<>(); + + public static float get(DebugProcessImpl process, Object producer) { + return getTimings(process).myMap.get(producer); + } + + public static void add(DebugProcessImpl process, Object producer, long overhead) { + getTimings(process).myMap.merge(producer, overhead, (old, value) -> old + value); + } + + private static OverheadTimings getTimings(DebugProcessImpl process) { + OverheadTimings data = process.getUserData(KEY); + if (data == null) { + data = new OverheadTimings(); + process.putUserData(KEY, data); + } + return data; + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java index eae7bf755316..9d001083a62c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java @@ -34,6 +34,7 @@ import com.intellij.debugger.requests.ClassPrepareRequestor; import com.intellij.debugger.requests.Requestor; import com.intellij.debugger.settings.DebuggerSettings; import com.intellij.debugger.ui.impl.watch.CompilingEvaluatorImpl; +import com.intellij.debugger.ui.OverheadTimings; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.project.Project; @@ -222,40 +223,47 @@ public abstract class Breakpoint

implements @Override public boolean processLocatableEvent(SuspendContextCommandImpl action, LocatableEvent event) throws EventProcessingException { + long start = System.currentTimeMillis(); + SuspendContextImpl context = action.getSuspendContext(); if (!isValid()) { context.getDebugProcess().getRequestsManager().deleteRequest(this); return false; } - String title = DebuggerBundle.message("title.error.evaluating.breakpoint.condition"); - try { - StackFrameProxyImpl frameProxy = context.getThread().frame(0); - if (frameProxy == null) { - // might be if the thread has been collected - return false; + String title = DebuggerBundle.message("title.error.evaluating.breakpoint.condition"); + + try { + StackFrameProxyImpl frameProxy = context.getThread().frame(0); + if (frameProxy == null) { + // might be if the thread has been collected + return false; + } + + EvaluationContextImpl evaluationContext = new EvaluationContextImpl(context, frameProxy, getThisObject(context, event)); + + if (!evaluateCondition(evaluationContext, event)) { + return false; + } + + title = DebuggerBundle.message("title.error.evaluating.breakpoint.action"); + runAction(evaluationContext, event); + } + catch (final EvaluateException ex) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + System.out.println(ex.getMessage()); + return false; + } + + throw new EventProcessingException(title, ex.getMessage(), ex); } - EvaluationContextImpl evaluationContext = new EvaluationContextImpl(context, frameProxy, getThisObject(context, event)); - - if (!evaluateCondition(evaluationContext, event)) { - return false; - } - - title = DebuggerBundle.message("title.error.evaluating.breakpoint.action"); - runAction(evaluationContext, event); + return true; } - catch (final EvaluateException ex) { - if(ApplicationManager.getApplication().isUnitTestMode()) { - System.out.println(ex.getMessage()); - return false; - } - - throw new EventProcessingException(title, ex.getMessage(), ex); + finally { + OverheadTimings.add(context.getDebugProcess(), this, System.currentTimeMillis() - start); } - - return true; } private void runAction(EvaluationContextImpl context, LocatableEvent event) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java index 4114fc9b8ffe..2ae707d0f12c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java @@ -56,13 +56,17 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.SpeedSearchComparator; import com.intellij.ui.TreeSpeedSearch; +import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink; +import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.settings.XDebuggerSettingsManager; import com.sun.jdi.*; import com.sun.jdi.event.Event; import com.sun.jdi.event.ExceptionEvent; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.TreeModelEvent; @@ -576,9 +580,6 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi return (ValueDescriptorImpl)getNode().getDescriptor(); } - @Override - public void setRemaining(int remaining) {} - @Override public void initChildrenArrayRenderer(ArrayRenderer renderer) {} @@ -591,6 +592,38 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi } updateUI(false); } + + @Override + public void addChildren(@NotNull XValueChildrenList children, boolean last) { + } + + @Override + public void tooManyChildren(int remaining) { + } + + @Override + public void setAlreadySorted(boolean alreadySorted) { + } + + @Override + public void setErrorMessage(@NotNull String errorMessage) { + } + + @Override + public void setErrorMessage(@NotNull String errorMessage, @Nullable XDebuggerTreeNodeHyperlink link) { + } + + @Override + public void setMessage(@NotNull String message, + @Nullable Icon icon, + @NotNull SimpleTextAttributes attributes, + @Nullable XDebuggerTreeNodeHyperlink link) { + } + + @Override + public boolean isObsolete() { + return false; + } } private class BuildStaticNodeCommand extends BuildNodeCommand { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java index f246c3785e38..7f01e744dec8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java @@ -21,21 +21,25 @@ import com.intellij.debugger.DebuggerManagerEx; import com.intellij.debugger.actions.ArrayAction; import com.intellij.debugger.engine.ContextUtil; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; +import com.intellij.debugger.engine.JavaValue; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; import com.intellij.debugger.impl.DebuggerUtilsEx; +import com.intellij.debugger.memory.utils.ErrorsValueGroup; import com.intellij.debugger.settings.NodeRendererSettings; import com.intellij.debugger.settings.ViewsGeneralSettings; import com.intellij.debugger.ui.impl.watch.ArrayElementDescriptorImpl; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; +import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl; import com.intellij.debugger.ui.tree.DebuggerTreeNode; import com.intellij.debugger.ui.tree.NodeDescriptor; import com.intellij.debugger.ui.tree.NodeDescriptorFactory; import com.intellij.debugger.ui.tree.ValueDescriptor; import com.intellij.icons.AllIcons; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; @@ -49,6 +53,7 @@ import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.IncorrectOperationException; import com.intellij.xdebugger.XExpression; import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink; +import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ArrayReference; @@ -159,7 +164,7 @@ public class ArrayRenderer extends NodeRendererImpl{ builder.setMessage(DebuggerBundle.message("message.node.elements.null.hidden"), null, SimpleTextAttributes.REGULAR_ATTRIBUTES, null); } if (!myForced && END_INDEX < arrayLength - 1) { - builder.setRemaining(arrayLength - 1 - END_INDEX); + builder.tooManyChildren(arrayLength - 1 - END_INDEX); } } } @@ -252,6 +257,7 @@ public class ArrayRenderer extends NodeRendererImpl{ int added = 0; if (arrayLength - 1 >= START_INDEX) { + ErrorsValueGroup errorsGroup = null; for (int idx = START_INDEX; idx < arrayLength; idx++) { try { if (DebuggerUtilsEx.evaluateBoolean(cachedEvaluator.getEvaluator(evaluationContext.getProject()), @@ -269,7 +275,17 @@ public class ArrayRenderer extends NodeRendererImpl{ } } catch (EvaluateException e) { - builder.addChildren(Collections.singletonList(nodeManager.createMessageNode(e.getMessage())), false); + if (errorsGroup == null) { + errorsGroup = new ErrorsValueGroup(); + builder.addChildren(XValueChildrenList.bottomGroup(errorsGroup), false); + } + JavaValue childValue = JavaValue + .create(null, + (ValueDescriptorImpl)descriptorFactory.getArrayItemDescriptor(builder.getParentDescriptor(), array, idx), + ((EvaluationContextImpl)evaluationContext), + nodeManager, + false); + errorsGroup.addErrorValue(e.getMessage(), childValue); } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java index 416089ed0de7..1e4b406add12 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java @@ -15,16 +15,15 @@ */ package com.intellij.debugger.ui.tree.render; -import com.intellij.debugger.ui.tree.*; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import com.intellij.debugger.ui.tree.DebuggerTreeNode; +import com.intellij.debugger.ui.tree.NodeDescriptorFactory; +import com.intellij.debugger.ui.tree.NodeManager; +import com.intellij.debugger.ui.tree.ValueDescriptor; +import com.intellij.xdebugger.frame.XCompositeNode; -import javax.swing.*; import java.util.List; -public interface ChildrenBuilder { +public interface ChildrenBuilder extends XCompositeNode { NodeDescriptorFactory getDescriptorManager(); NodeManager getNodeManager(); @@ -37,13 +36,10 @@ public interface ChildrenBuilder { setChildren(children); } - default void setMessage(@NotNull String message, - @Nullable Icon icon, - @NotNull SimpleTextAttributes attributes, - @Nullable XDebuggerTreeNodeHyperlink link) { + @Deprecated + default void setRemaining(int remaining) { + tooManyChildren(remaining); } - void setRemaining(int remaining); - void initChildrenArrayRenderer(ArrayRenderer renderer); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index 5138d2cabf91..7299220a847a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -195,7 +195,9 @@ public class HighlightMethodUtil { if (returnType.equals(substitutedSuperReturnType)) return null; if (!(returnType instanceof PsiPrimitiveType) && substitutedSuperReturnType.getDeepComponentType() instanceof PsiClassType) { - if (isJdk15 && TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType)) { + if (isJdk15 && LambdaUtil.performWithSubstitutedParameterBounds(methodSignature.getTypeParameters(), + methodSignature.getSubstitutor(), + () -> TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType))) { return null; } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index af791a1cf50a..d58fe33d0cfd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -1579,11 +1579,13 @@ public class HighlightUtil extends HighlightUtilBase { final PsiElement parent = expr.getParent(); final PsiElement resolved = parent instanceof PsiReferenceExpression ? ((PsiReferenceExpression)parent).resolve() : null; + PsiClass containingClass = + ObjectUtils.notNull(resolved instanceof PsiMethod ? ((PsiMethod)resolved).getContainingClass() : null, aClass); for (PsiClass superClass : classT.getSupers()) { - if (superClass.isInheritor(aClass, true)) { + if (superClass.isInheritor(containingClass, true)) { String cause = null; - if (superClass.isInterface()) { - cause = "redundant interface " + format(aClass) + " is extended by "; + if (superClass.isInheritor(aClass, true) && superClass.isInterface()) { + cause = "redundant interface " + format(containingClass) + " is extended by "; } else if (resolved instanceof PsiMethod && MethodSignatureUtil.findMethodBySuperMethod(superClass, (PsiMethod)resolved, true) != resolved) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java index 6cfc3fc07868..b7ff8c8954f5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.util.RefactoringChangeUtil; +import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; public class QualifySuperArgumentFix extends QualifyThisOrSuperArgumentFix { @@ -50,7 +51,14 @@ public class QualifySuperArgumentFix extends QualifyThisOrSuperArgumentFix { final PsiExpression superQualifierCopy = copy.getMethodExpression().getQualifierExpression(); LOG.assertTrue(superQualifierCopy != null); superQualifierCopy.delete(); - PsiMethod method = ((PsiMethodCallExpression)elementFactory.createExpressionFromText(copy.getText(), superClass)).resolveMethod(); + PsiMethod method; + try { + method = ((PsiMethodCallExpression)elementFactory.createExpressionFromText(copy.getText(), superClass)).resolveMethod(); + } + catch (IncorrectOperationException e) { + LOG.info(e); + return; + } if (method != null && !method.hasModifierProperty(PsiModifier.ABSTRACT)) { QuickFixAction.registerQuickFixAction(highlightInfo, new QualifySuperArgumentFix(expr, superClass)); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/BoxForComparisonInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/BoxForComparisonInspection.java deleted file mode 100644 index b7f5db473fe7..000000000000 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/BoxForComparisonInspection.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2000-2017 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.codeInspection; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.siyeh.ig.psiutils.CommentTracker; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author Tagir Valeev - */ -public class BoxForComparisonInspection extends BaseJavaBatchLocalInspectionTool { - @NotNull - @Override - public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { - if(!PsiUtil.getLanguageLevel(holder.getFile()).isAtLeast(LanguageLevel.JDK_1_4)) { - return PsiElementVisitor.EMPTY_VISITOR; - } - return new JavaElementVisitor() { - @Override - public void visitMethodCallExpression(PsiMethodCallExpression call) { - PsiElement nameElement = call.getMethodExpression().getReferenceNameElement(); - if (nameElement == null) return; - String name = nameElement.getText(); - if (!"compareTo".equals(name)) return; - PsiExpression[] args = call.getArgumentList().getExpressions(); - if (args.length != 1) return; - PsiExpression arg = args[0]; - PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); - if (qualifier == null) return; - PsiClassType boxedType = getBoxedType(call); - if (boxedType == null) return; - PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType); - if (primitiveType == null || !PsiType.DOUBLE.equals(primitiveType) && !PsiType.FLOAT.equals(primitiveType) && - !PsiUtil.isLanguageLevel7OrHigher(call)) { - return; - } - PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier); - if (left == null) return; - PsiExpression right = extractPrimitive(boxedType, primitiveType, arg); - if (right == null) return; - holder.registerProblem(nameElement, "Can be replaced with '" + boxedType.getClassName() + ".compare'", - new ReplaceWithPrimitiveCompareFix(boxedType.getCanonicalText())); - } - }; - } - - @Nullable - static PsiClassType getBoxedType(PsiMethodCallExpression call) { - PsiMethod method = call.resolveMethod(); - if (method == null) return null; - PsiClass aClass = method.getContainingClass(); - if (aClass == null) return null; - return JavaPsiFacade.getElementFactory(call.getProject()).createType(aClass); - } - - @Nullable - static PsiExpression extractPrimitive(PsiClassType type, PsiPrimitiveType primitiveType, PsiExpression expression) { - expression = PsiUtil.skipParenthesizedExprDown(expression); - if (expression == null) return null; - if (primitiveType.equals(expression.getType())) { - return expression; - } - if (expression instanceof PsiMethodCallExpression) { - PsiMethodCallExpression call = (PsiMethodCallExpression)expression; - if (!"valueOf".equals(call.getMethodExpression().getReferenceName())) return null; - PsiExpression[] args = call.getArgumentList().getExpressions(); - if (args.length != 1) return null; - PsiMethod method = call.resolveMethod(); - if (method == null || type.resolve() != method.getContainingClass()) return null; - return checkPrimitive(args[0]); - } - if (expression instanceof PsiTypeCastExpression) { - PsiTypeCastExpression cast = (PsiTypeCastExpression)expression; - if (!type.equals(cast.getType())) return null; - return checkPrimitive(cast.getOperand()); - } - if (expression instanceof PsiNewExpression) { - PsiNewExpression newExpression = (PsiNewExpression)expression; - if (!type.equals(newExpression.getType())) return null; - PsiExpressionList argumentList = newExpression.getArgumentList(); - if (argumentList == null) return null; - PsiExpression[] args = argumentList.getExpressions(); - if (args.length != 1) return null; - if (!(args[0].getType() instanceof PsiPrimitiveType)) return null; - return checkPrimitive(args[0]); - } - return null; - } - - private static PsiExpression checkPrimitive(PsiExpression expression) { - return expression != null && expression.getType() instanceof PsiPrimitiveType ? expression : null; - } - - private static class ReplaceWithPrimitiveCompareFix implements LocalQuickFix { - private String myClassName; - - public ReplaceWithPrimitiveCompareFix(String className) { - myClassName = className; - } - - @Nls - @NotNull - @Override - public String getName() { - return "Replace with '" + StringUtil.getShortName(myClassName) + ".compare'"; - } - - @Nls - @NotNull - @Override - public String getFamilyName() { - return "Replace with static 'compare' method"; - } - - @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiMethodCallExpression.class); - if (call == null) return; - PsiClassType boxedType = getBoxedType(call); - if (boxedType == null) return; - PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType); - if (primitiveType == null) return; - PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); - if (qualifier == null) return; - PsiExpression[] args = call.getArgumentList().getExpressions(); - if (args.length != 1) return; - PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier); - if (left == null) return; - PsiExpression right = extractPrimitive(boxedType, primitiveType, args[0]); - if (right == null) return; - - CommentTracker ct = new CommentTracker(); - ct.replaceAndRestoreComments(call, boxedType.getCanonicalText() + ".compare(" + ct.text(left) + "," + ct.text(right) + ")"); - } - } -} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java new file mode 100644 index 000000000000..154d14ccdfa4 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/UseCompareMethodInspection.java @@ -0,0 +1,380 @@ +/* + * Copyright 2000-2017 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.codeInspection; + +import com.intellij.codeInsight.PsiEquivalenceUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.ig.psiutils.CommentTracker; +import com.siyeh.ig.psiutils.ControlFlowUtils; +import com.siyeh.ig.psiutils.ExpressionUtils; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.intellij.util.ObjectUtils.tryCast; + +/** + * @author Tagir Valeev + */ +public class UseCompareMethodInspection extends BaseJavaBatchLocalInspectionTool { + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + if (!PsiUtil.getLanguageLevel(holder.getFile()).isAtLeast(LanguageLevel.JDK_1_4)) { + return PsiElementVisitor.EMPTY_VISITOR; + } + return new JavaElementVisitor() { + @Override + public void visitMethodCallExpression(PsiMethodCallExpression call) { + CompareInfo info = fromCall(call); + PsiElement nameElement = call.getMethodExpression().getReferenceNameElement(); + if (info != null && nameElement != null) { + register(info, nameElement); + } + } + + @Override + public void visitIfStatement(PsiIfStatement statement) { + CompareInfo info = fromIf(statement); + PsiElement keyword = statement.getFirstChild(); + if (info != null && keyword != null) { + register(info, keyword); + } + } + + @Override + public void visitConditionalExpression(PsiConditionalExpression expression) { + CompareInfo info = fromTernary(expression); + if (info != null) { + register(info, expression); + } + } + + private void register(CompareInfo info, PsiElement nameElement) { + holder.registerProblem(nameElement, "Can be replaced with '" + info.myClass.getClassName() + ".compare'", + new ReplaceWithPrimitiveCompareFix(info.myClass.getCanonicalText())); + } + }; + } + + private static CompareInfo fromIf(PsiIfStatement ifStatement) { + PsiExpression firstCondition = ifStatement.getCondition(); + if (firstCondition == null) return null; + PsiIfStatement elseIfStatement = tryCast(getElse(ifStatement), PsiIfStatement.class); + if (elseIfStatement == null) return null; + PsiExpression secondCondition = elseIfStatement.getCondition(); + if (secondCondition == null) return null; + PsiStatement firstStatement = ControlFlowUtils.stripBraces(ifStatement.getThenBranch()); + if (firstStatement == null) return null; + PsiStatement secondStatement = ControlFlowUtils.stripBraces(elseIfStatement.getThenBranch()); + if (secondStatement == null) return null; + PsiStatement thirdStatement = getElse(elseIfStatement); + if (thirdStatement == null) return null; + + Map result = new HashMap<>(3); + // like if(...) return 1; else if(...) return -1; return 0; + if (firstStatement instanceof PsiReturnStatement) { + if (!(secondStatement instanceof PsiReturnStatement) || !(thirdStatement instanceof PsiReturnStatement)) return null; + PsiExpression firstValue = ((PsiReturnStatement)firstStatement).getReturnValue(); + if (!storeCondition(result, firstCondition, firstValue)) return null; + if (!storeCondition(result, secondCondition, ((PsiReturnStatement)secondStatement).getReturnValue())) return null; + if (!storeCondition(result, null, ((PsiReturnStatement)thirdStatement).getReturnValue())) return null; + return fromMap(result, firstValue, firstStatement); + } + // like if(...) x = 1; else if(...) x = -1; else x = 0; + PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(firstStatement); + if (assignment == null) return null; + PsiReferenceExpression ref = tryCast(assignment.getLExpression(), PsiReferenceExpression.class); + if (ref == null) return null; + PsiVariable variable = tryCast(ref.resolve(), PsiVariable.class); + if (variable == null) return null; + PsiExpression firstExpression = assignment.getRExpression(); + if (!storeCondition(result, firstCondition, firstExpression)) return null; + if (!storeCondition(result, secondCondition, ExpressionUtils.getAssignmentTo(secondStatement, variable))) return null; + if (!storeCondition(result, null, ExpressionUtils.getAssignmentTo(thirdStatement, variable))) return null; + return fromMap(result, firstExpression, assignment); + } + + private static PsiStatement getElse(PsiIfStatement ifStatement) { + PsiStatement branch = ControlFlowUtils.stripBraces(ifStatement.getElseBranch()); + if (branch != null) return branch; + PsiStatement thenBranch = ControlFlowUtils.stripBraces(ifStatement.getThenBranch()); + if (!(thenBranch instanceof PsiReturnStatement)) return null; + PsiElement next = PsiTreeUtil.skipSiblingsForward(ifStatement, PsiComment.class, PsiWhiteSpace.class); + return tryCast(next, PsiStatement.class); + } + + @Nullable + private static Map extractConditions(PsiConditionalExpression ternary) { + Map result = new HashMap<>(3); + if (!storeCondition(result, ternary.getCondition(), ternary.getThenExpression())) return null; + PsiExpression elseExpression = PsiUtil.skipParenthesizedExprDown(ternary.getElseExpression()); + if (elseExpression instanceof PsiConditionalExpression) { + Map m = extractConditions((PsiConditionalExpression)elseExpression); + if (m == null) return null; + result.putAll(m); + return result; + } + return storeCondition(result, null, elseExpression) ? result : null; + } + + @Contract("_, _, null -> false") + private static boolean storeCondition(@NotNull Map result, + @Nullable PsiExpression condition, + @Nullable PsiExpression expression) { + if (expression == null) return false; + Object thenValue = ExpressionUtils.computeConstantExpression(expression); + if (!(thenValue instanceof Integer) || Math.abs((Integer)thenValue) > 1) return false; + result.put((Integer)thenValue, condition); + return true; + } + + private static CompareInfo fromTernary(PsiConditionalExpression ternary) { + if (!PsiType.INT.equals(ternary.getType())) return null; + Map map = extractConditions(ternary); + return fromMap(map, ternary, ternary); + } + + private static CompareInfo fromMap(@Nullable Map map, + @NotNull PsiExpression expression, + @NotNull PsiElement template) { + if (map == null || map.size() != 3) { + return null; + } + PsiExpression lt = map.get(-1); + Pair ltPair = getOperands(lt, JavaTokenType.LT); + if (lt != null && ltPair == null) return null; + + PsiExpression gt = map.get(1); + Pair gtPair = getOperands(gt, JavaTokenType.GT); + if ((gt != null || ltPair == null) && gtPair == null) return null; + + if (ltPair != null && gtPair != null) { + if (!PsiEquivalenceUtil.areElementsEquivalent(ltPair.getFirst(), gtPair.getFirst())) return null; + if (!PsiEquivalenceUtil.areElementsEquivalent(ltPair.getSecond(), gtPair.getSecond())) return null; + } + Pair canonicalPair = ltPair == null ? gtPair : ltPair; + PsiType leftType = canonicalPair.getFirst().getType(); + PsiType rightType = canonicalPair.getSecond().getType(); + if (!isTypeConvertible(leftType, expression) || !leftType.equals(rightType)) return null; + + PsiExpression eq = map.get(0); + Pair eqPair = getOperands(eq, JavaTokenType.EQEQ); + if (eq != null && eqPair == null) return null; + if (eqPair != null) { + if ((!PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getFirst(), eqPair.getFirst()) || + !PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getSecond(), eqPair.getSecond())) && + (!PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getFirst(), eqPair.getSecond()) || + !PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getSecond(), eqPair.getFirst()))) { + return null; + } + } + PsiClassType boxedType = ((PsiPrimitiveType)leftType).getBoxedType(expression); + return new CompareInfo(template, expression, canonicalPair.getFirst(), canonicalPair.getSecond(), boxedType); + } + + private static Pair getOperands(PsiExpression expression, IElementType expectedToken) { + expression = PsiUtil.skipParenthesizedExprDown(expression); + if (!(expression instanceof PsiBinaryExpression)) return null; + PsiBinaryExpression binOp = (PsiBinaryExpression)expression; + PsiExpression left = PsiUtil.skipParenthesizedExprDown(binOp.getLOperand()); + PsiExpression right = PsiUtil.skipParenthesizedExprDown(binOp.getROperand()); + if (left == null || right == null) return null; + if (binOp.getOperationTokenType().equals(expectedToken)) { + return Pair.create(left, right); + } + if (expectedToken.equals(JavaTokenType.GT) && binOp.getOperationTokenType().equals(JavaTokenType.LT) || + expectedToken.equals(JavaTokenType.LT) && binOp.getOperationTokenType().equals(JavaTokenType.GT)) { + return Pair.create(right, left); + } + return null; + } + + @Contract("null -> null") + private static CompareInfo fromCall(PsiMethodCallExpression call) { + if (call == null) return null; + PsiElement nameElement = call.getMethodExpression().getReferenceNameElement(); + if (nameElement == null) return null; + String name = nameElement.getText(); + if (!"compareTo".equals(name)) return null; + PsiExpression[] args = call.getArgumentList().getExpressions(); + if (args.length != 1) return null; + PsiExpression arg = args[0]; + PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); + if (qualifier == null) return null; + PsiClassType boxedType = getBoxedType(call); + if (boxedType == null) return null; + PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType); + if (!isTypeConvertible(primitiveType, call)) return null; + PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier); + if (left == null) return null; + PsiExpression right = extractPrimitive(boxedType, primitiveType, arg); + if (right == null) return null; + return new CompareInfo(call, call, left, right, boxedType); + } + + @Nullable + static PsiClassType getBoxedType(PsiMethodCallExpression call) { + PsiMethod method = call.resolveMethod(); + if (method == null) return null; + PsiClass aClass = method.getContainingClass(); + if (aClass == null) return null; + return JavaPsiFacade.getElementFactory(call.getProject()).createType(aClass); + } + + @Nullable + static PsiExpression extractPrimitive(PsiClassType type, PsiPrimitiveType primitiveType, PsiExpression expression) { + expression = PsiUtil.skipParenthesizedExprDown(expression); + if (expression == null) return null; + if (primitiveType.equals(expression.getType())) { + return expression; + } + if (expression instanceof PsiMethodCallExpression) { + PsiMethodCallExpression call = (PsiMethodCallExpression)expression; + if (!"valueOf".equals(call.getMethodExpression().getReferenceName())) return null; + PsiExpression[] args = call.getArgumentList().getExpressions(); + if (args.length != 1) return null; + PsiMethod method = call.resolveMethod(); + if (method == null || type.resolve() != method.getContainingClass()) return null; + return checkPrimitive(args[0]); + } + if (expression instanceof PsiTypeCastExpression) { + PsiTypeCastExpression cast = (PsiTypeCastExpression)expression; + if (!type.equals(cast.getType())) return null; + return checkPrimitive(cast.getOperand()); + } + if (expression instanceof PsiNewExpression) { + PsiNewExpression newExpression = (PsiNewExpression)expression; + if (!type.equals(newExpression.getType())) return null; + PsiExpressionList argumentList = newExpression.getArgumentList(); + if (argumentList == null) return null; + PsiExpression[] args = argumentList.getExpressions(); + if (args.length != 1) return null; + if (!(args[0].getType() instanceof PsiPrimitiveType)) return null; + return checkPrimitive(args[0]); + } + return null; + } + + private static PsiExpression checkPrimitive(PsiExpression expression) { + return expression != null && expression.getType() instanceof PsiPrimitiveType ? expression : null; + } + + @Contract("null, _ -> false") + private static boolean isTypeConvertible(PsiType type, PsiElement context) { + return type instanceof PsiPrimitiveType && (PsiType.DOUBLE.equals(type) || + PsiType.FLOAT.equals(type) || + PsiUtil.isLanguageLevel7OrHigher(context)); + } + + static class CompareInfo { + final PsiElement myTemplate; + final PsiExpression myToReplace; + final PsiExpression myLeft; + final PsiExpression myRight; + final PsiClassType myClass; + + CompareInfo(PsiElement template, + PsiExpression toReplace, + PsiExpression left, + PsiExpression right, + PsiClassType aClass) { + myTemplate = template; + myToReplace = toReplace; + myLeft = left; + myRight = right; + myClass = aClass; + } + + private void replace(PsiElement toReplace, CommentTracker ct) { + String replacement = this.myClass.getCanonicalText() + ".compare(" + ct.text(this.myLeft) + "," + ct.text(this.myRight) + ")"; + if(toReplace == myTemplate) { + ct.replaceAndRestoreComments(myToReplace, replacement); + } else { + ct.replace(myToReplace, replacement); + ct.replaceAndRestoreComments(toReplace, myTemplate); + } + } + } + + private static class ReplaceWithPrimitiveCompareFix implements LocalQuickFix { + private String myClassName; + + public ReplaceWithPrimitiveCompareFix(String className) { + myClassName = className; + } + + @Nls + @NotNull + @Override + public String getName() { + return "Replace with '" + StringUtil.getShortName(myClassName) + ".compare'"; + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Replace with static 'compare' method"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiElement element = descriptor.getStartElement(); + PsiElement toReplace; + List toDelete = new ArrayList<>(); + CompareInfo info; + if (element instanceof PsiConditionalExpression) { + toReplace = element; + info = fromTernary((PsiConditionalExpression)element); + } + else { + PsiElement parent = element.getParent(); + if (parent instanceof PsiIfStatement) { + toReplace = parent; + info = fromIf((PsiIfStatement)parent); + PsiStatement elseIf = getElse((PsiIfStatement)parent); + toDelete.add(elseIf); + if(elseIf instanceof PsiIfStatement) { + toDelete.add(getElse((PsiIfStatement)elseIf)); + } + } else { + PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class); + info = fromCall(call); + toReplace = call; + } + } + if (info == null) return; + CommentTracker ct = new CommentTracker(); + info.replace(toReplace, ct); + StreamEx.of(toDelete).nonNull().filter(PsiElement::isValid).forEach(e -> new CommentTracker().deleteAndRestoreComments(e)); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java index db8722c06f12..6332fd8ed0af 100644 --- a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java @@ -16,22 +16,35 @@ package com.intellij.codeInsight; import com.intellij.codeInspection.dataFlow.HardcodedContracts; +import com.intellij.codeInspection.dataFlow.Nullness; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; -import com.intellij.psi.PsiElement; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.util.containers.ContainerUtil; +import one.util.streamex.StreamEx; import org.jdom.Element; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerNotNullableSerializer; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @State(name = "NullableNotNullManager") public class NullableNotNullManagerImpl extends NullableNotNullManager implements PersistentStateComponent { - public NullableNotNullManagerImpl() { + public static final String TYPE_QUALIFIER_NICKNAME = "javax.annotation.meta.TypeQualifierNickname"; + + public NullableNotNullManagerImpl(Project project) { + super(project); myNotNulls.addAll(getPredefinedNotNulls()); } @@ -80,4 +93,64 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement LOG.error(e); } } + + private List getAllNullabilityNickNames() { + if (!getNotNulls().contains(JAVAX_ANNOTATION_NONNULL)) { + return Collections.emptyList(); + } + return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> { + List result = new ArrayList<>(); + GlobalSearchScope scope = GlobalSearchScope.allScope(myProject); + for (PsiClass tqNick : JavaPsiFacade.getInstance(myProject).findClasses(TYPE_QUALIFIER_NICKNAME, scope)) { + result.addAll(ContainerUtil.findAll(MetaAnnotationUtil.getChildren(tqNick, scope), candidate -> { + String qname = candidate.getQualifiedName(); + if (qname == null || qname.startsWith("javax.annotation.")) return false; + return getNickNamedNullability(candidate) != Nullness.UNKNOWN; + })); + } + return CachedValueProvider.Result.create(result, PsiModificationTracker.MODIFICATION_COUNT); + }); + } + + private static Nullness getNickNamedNullability(@NotNull PsiClass psiClass) { + if (AnnotationUtil.findAnnotation(psiClass, TYPE_QUALIFIER_NICKNAME) == null) return Nullness.UNKNOWN; + + PsiAnnotation nonNull = AnnotationUtil.findAnnotation(psiClass, JAVAX_ANNOTATION_NONNULL); + return nonNull != null ? extractNullityFromWhenValue(nonNull) : Nullness.UNKNOWN; + } + + @NotNull + private static Nullness extractNullityFromWhenValue(PsiAnnotation nonNull) { + PsiAnnotationMemberValue when = nonNull.findAttributeValue("when"); + if (when instanceof PsiReferenceExpression) { + String refName = ((PsiReferenceExpression)when).getReferenceName(); + if ("ALWAYS".equals(refName)) { + return Nullness.NOT_NULL; + } + if ("MAYBE".equals(refName) || "NEVER".equals(refName)) { + return Nullness.NULLABLE; + } + } + return Nullness.UNKNOWN; + } + + private List filterNickNames(Nullness nullness) { + return StreamEx.of(getAllNullabilityNickNames()).filter(c -> getNickNamedNullability(c) == nullness).map(PsiClass::getQualifiedName).toList(); + } + + @NotNull + @Override + protected List getNullablesWithNickNames() { + return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> + CachedValueProvider.Result.create(ContainerUtil.concat(getNullables(), filterNickNames(Nullness.NULLABLE)), + PsiModificationTracker.MODIFICATION_COUNT)); + } + + @NotNull + @Override + protected List getNotNullsWithNickNames() { + return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> + CachedValueProvider.Result.create(ContainerUtil.concat(getNotNulls(), filterNickNames(Nullness.NOT_NULL)), + PsiModificationTracker.MODIFICATION_COUNT)); + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java index 7f3f8a33b0af..40a9d3b70af7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java @@ -19,7 +19,6 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementDecorator; import com.intellij.codeInsight.lookup.LookupElementPresentation; import com.intellij.codeInsight.lookup.TypedLookupItem; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.util.ClassConditionKey; import com.intellij.openapi.util.Key; @@ -39,19 +38,23 @@ import java.util.Set; */ public class JavaChainLookupElement extends LookupElementDecorator implements TypedLookupItem { public static final Key CHAIN_QUALIFIER = Key.create("CHAIN_QUALIFIER"); - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaChainLookupElement"); public static final ClassConditionKey CLASS_CONDITION_KEY = ClassConditionKey.create(JavaChainLookupElement.class); private final LookupElement myQualifier; + private final String mySeparator; public JavaChainLookupElement(LookupElement qualifier, LookupElement main) { + this(qualifier, main, "."); + } + public JavaChainLookupElement(LookupElement qualifier, LookupElement main, String separator) { super(main); myQualifier = qualifier; + mySeparator = separator; } @NotNull @Override public String getLookupString() { - return maybeAddParentheses(myQualifier.getLookupString()) + "." + getDelegate().getLookupString(); + return maybeAddParentheses(myQualifier.getLookupString()) + mySeparator + getDelegate().getLookupString(); } public LookupElement getQualifier() { @@ -70,7 +73,7 @@ public class JavaChainLookupElement extends LookupElementDecorator 0 && statements[0] instanceof PsiExpressionStatement) { + PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression(); + if (expression instanceof PsiMethodCallExpression && + PsiKeyword.THIS.equals(((PsiMethodCallExpression)expression).getMethodExpression().getReferenceName())) { + return false; + } + } + } + else { + return false; + } + } setText(CodeInsightBundle.message("intention.assign.field.from.parameter.text", field.getName())); return true; diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java index dfb800f41c1a..f536baaef798 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSwitchIntention.java @@ -33,40 +33,33 @@ public class CreateSwitchIntention extends BaseElementAtCaretIntentionAction { public static final String TEXT = "Create switch statement"; @Override - public void invoke(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) throws IncorrectOperationException { - final PsiExpressionStatement expressionStatement = resolveExpressionStatement(element); - final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); - PsiSwitchStatement switchStatement = (PsiSwitchStatement)elementFactory - .createStatementFromText(String.format("switch (%s) {}", expressionStatement.getExpression().getText()), null); + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + PsiExpressionStatement expressionStatement = PsiTreeUtil.getParentOfType(element, PsiExpressionStatement.class, false); + PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); + String valueToSwitch = expressionStatement.getExpression().getText(); + PsiSwitchStatement switchStatement = (PsiSwitchStatement)elementFactory.createStatementFromText("switch (" + valueToSwitch + ") {}", null); switchStatement = (PsiSwitchStatement)expressionStatement.replace(switchStatement); CodeStyleManager.getInstance(project).reformat(switchStatement); - final PsiJavaToken lBrace = switchStatement.getBody().getLBrace(); + PsiJavaToken lBrace = switchStatement.getBody().getLBrace(); editor.getCaretModel().moveToOffset(lBrace.getTextOffset() + lBrace.getTextLength()); } @Override - public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) { - final PsiExpressionStatement expressionStatement = resolveExpressionStatement(element); - return expressionStatement != null && isValidTypeForSwitch(expressionStatement.getExpression().getType(), expressionStatement); + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + PsiExpressionStatement expressionStatement = PsiTreeUtil.getParentOfType(element, PsiExpressionStatement.class, false); + return expressionStatement != null && + expressionStatement.getParent() instanceof PsiCodeBlock && + isValidTypeForSwitch(expressionStatement.getExpression().getType(), expressionStatement); } - private static PsiExpressionStatement resolveExpressionStatement(final PsiElement element) { - if (element instanceof PsiExpressionStatement) { - return (PsiExpressionStatement)element; - } else { - final PsiStatement psiStatement = PsiTreeUtil.getParentOfType(element, PsiStatement.class); - return psiStatement instanceof PsiExpressionStatement ? (PsiExpressionStatement)psiStatement : null; - } - } - - private static boolean isValidTypeForSwitch(@Nullable final PsiType type, final PsiElement context) { + private static boolean isValidTypeForSwitch(@Nullable PsiType type, PsiElement context) { if (type == null) { return false; } if (type instanceof PsiClassType) { - final PsiClass resolvedClass = ((PsiClassType)type).resolve(); + PsiClass resolvedClass = ((PsiClassType)type).resolve(); if (resolvedClass == null) { return false; } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java index aa644cd96a20..8168ae2fe60a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java @@ -112,9 +112,9 @@ public final class FieldFromParameterUtils { for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), false)) { if (!(reference instanceof PsiReferenceExpression)) continue; final PsiReferenceExpression expression = (PsiReferenceExpression)reference; - if (!(expression.getParent() instanceof PsiAssignmentExpression)) continue; - final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression.getParent(); - if (assignmentExpression.getRExpression() != expression) continue; + PsiAssignmentExpression assignmentExpression = PsiTreeUtil.getParentOfType(expression, PsiAssignmentExpression.class, true, PsiClass.class); + if (assignmentExpression == null) continue; + if (!PsiTreeUtil.isAncestor(assignmentExpression.getRExpression(), expression, false)) continue; final PsiExpression lExpression = assignmentExpression.getLExpression(); if (!(lExpression instanceof PsiReferenceExpression)) continue; final PsiElement element = ((PsiReferenceExpression)lExpression).resolve(); diff --git a/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java b/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java index 95c2f9ab09b0..3d867c9174dd 100644 --- a/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java @@ -83,18 +83,12 @@ public class MakeVoidQuickFix implements LocalQuickFix { for (final PsiMethod oMethod : OverridingMethodsSearch.search(psiMethod)) { replaceReturnStatements(oMethod); } - final PsiParameter[] params = psiMethod.getParameterList().getParameters(); - final ParameterInfoImpl[] infos = new ParameterInfoImpl[params.length]; - for (int i = 0; i < params.length; i++) { - PsiParameter param = params[i]; - infos[i] = new ParameterInfoImpl(i, param.getName(), param.getType()); - } - + final ChangeSignatureProcessor csp = new ChangeSignatureProcessor(project, psiMethod, false, null, psiMethod.getName(), PsiType.VOID, - infos); + ParameterInfoImpl.fromMethod(psiMethod)); csp.run(); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java b/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java similarity index 87% rename from java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java rename to java/java-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java index bd2202129b37..cefbd93158c4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RawTypeCanBeGenericInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -18,8 +18,11 @@ package com.intellij.codeInspection.miscGenerics; import com.intellij.codeInspection.*; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.refactoring.typeMigration.TypeMigrationProcessor; +import com.intellij.refactoring.typeMigration.TypeMigrationRules; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -103,18 +106,21 @@ public class RawTypeCanBeGenericInspection extends BaseJavaBatchLocalInspectionT return InspectionsBundle.message("inspection.raw.variable.type.can.be.generic.family.quickfix"); } + @Override + public boolean startInWriteAction() { + return false; + } + @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiElement element = descriptor.getStartElement().getParent(); if (element instanceof PsiVariable) { final PsiVariable variable = (PsiVariable)element; - final PsiTypeElement typeElement = variable.getTypeElement(); - if (typeElement != null) { - final PsiType type = getSuggestedType(variable); - if (type != null) { - final PsiElementFactory factory = JavaPsiFacade.getInstance(variable.getProject()).getElementFactory(); - typeElement.replace(factory.createTypeElement(type)); - } + final PsiType type = getSuggestedType(variable); + if (type != null) { + final TypeMigrationRules rules = new TypeMigrationRules(); + rules.setBoundScope(PsiSearchHelper.SERVICE.getInstance(project).getUseScope(variable)); + TypeMigrationProcessor.runHighlightingTypeMigration(project, null, rules, variable, type, false); } } } diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java index 4db61720ca88..e71c07de7568 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java @@ -309,7 +309,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI * from arguments of MethodType.methodType(Class...) and MethodType.genericMethodType(int, boolean?) */ @Nullable - private static ReflectiveSignature composeMethodSignature(@Nullable PsiExpression methodTypeExpression) { + public static ReflectiveSignature composeMethodSignature(@Nullable PsiExpression methodTypeExpression) { final PsiExpression typeDefinition = findDefinition(methodTypeExpression); if (typeDefinition instanceof PsiMethodCallExpression) { final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)typeDefinition; diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java index 04bd9a3dca84..6f3515fceafe 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java @@ -20,6 +20,7 @@ import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -131,22 +132,34 @@ public class JavaReflectionInvocationInspection extends BaseJavaBatchLocalInspec if (definition instanceof PsiMethodCallExpression) { final PsiMethodCallExpression definitionCall = (PsiMethodCallExpression)definition; if (methodPredicate.test(definitionCall)) { - final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions(); - - if (arguments.length == argumentOffset + 1) { - final PsiExpression[] arrayElements = getVarargAsArray(arguments[argumentOffset]); - if (arrayElements != null) { - return Arrays.asList(arrayElements); - } - } - if (arguments.length >= argumentOffset) { - return Arrays.asList(arguments).subList(argumentOffset, arguments.length); - } + return getRequiredMethodArguments(definitionCall, argumentOffset); } } return null; } + private static List getRequiredMethodArguments(@NotNull PsiMethodCallExpression definitionCall, int argumentOffset) { + final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions(); + + if (arguments.length == argumentOffset + 1) { + final PsiExpression[] arrayElements = getVarargAsArray(arguments[argumentOffset]); + if (arrayElements != null) { + return Arrays.asList(arrayElements); + } + } + if (arguments.length >= argumentOffset) { + return Arrays.asList(arguments).subList(argumentOffset, arguments.length); + } + return null; + } + + @Nullable + public static List getReflectionMethodParameterTypes(@NotNull PsiMethodCallExpression definitionCall, + int argumentOffset) { + List arguments = getRequiredMethodArguments(definitionCall, argumentOffset); + return arguments != null ? ContainerUtil.map(arguments, type -> getReflectiveType(type)) : null; + } + @Nullable static Arguments getActualMethodArguments(PsiExpression[] arguments, int argumentOffset, boolean allowVarargAsArray) { if (allowVarargAsArray && arguments.length == argumentOffset + 1) { diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java index 951770eb6d56..90213c251cfa 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java @@ -279,6 +279,11 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp final List argumentTypes = ContainerUtil.map(methodArguments.expressions, JavaReflectionReferenceUtil::getReflectiveType); + return matchMethod(methods, argumentTypes); + } + + @Nullable + public static PsiMethod matchMethod(@NotNull PsiMethod[] methods, @NotNull List argumentTypes) { int mismatchCount = Integer.MAX_VALUE; PsiMethod bestGuess = null; for (PsiMethod method : methods) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java deleted file mode 100644 index 49f9b8dd178e..000000000000 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.impl.source.codeStyle; - -import com.intellij.lang.ASTNode; -import com.intellij.lang.Language; -import com.intellij.lang.java.JavaLanguage; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.psi.formatter.FormatterUtil; -import com.intellij.psi.impl.source.tree.TreeUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * This class handles a use-case when reformatted text conflicts with 'use tab' code style setting. E.g. target text uses - * tabs for indentation but our code style is configured to use spaces. - *

- * We already have corresponding support at the block level but it's possible that multiline text is treated as a single block, - * i.e. all its internal indents are not visible to the formatter. That's why current class is introduced. - *

- * Thread-safe. - * - * @author Denis Zhdanov - * @since 8/1/12 2:38 PM - */ -public class TabPostFormatProcessor implements PostFormatProcessor { - - @Override - public PsiElement processElement(@NotNull PsiElement source, @NotNull CodeStyleSettings settings) { - doProcess(source, TextRange.from(source.getTextRange().getStartOffset(), source.getTextLength()), settings); - return source; - } - - @NotNull - @Override - public TextRange processText(@NotNull PsiFile source, @NotNull TextRange rangeToReformat, @NotNull CodeStyleSettings settings) { - return doProcess(source, rangeToReformat, settings); - } - - @NotNull - private static TextRange doProcess(@NotNull PsiElement source, @NotNull TextRange range, @NotNull CodeStyleSettings settings) { - ASTNode node = source.getNode(); - if (node == null) { - return range; - } - - Language language = source.getLanguage(); - if (language != JavaLanguage.INSTANCE) { - // We had the only complaint for tabs not being converted to spaces for now. It was for the java code which has - // a single block for the multi-line comment. This check should be removed if it is decided to generalize - // this logic to other languages as well. - return range; - } - - if (!source.isValid()) return range; - PsiFile file = source.getContainingFile(); - CommonCodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptionsByFile(file, range); - - boolean useTabs = indentOptions.USE_TAB_CHARACTER; - boolean smartTabs = indentOptions.SMART_TABS; - int tabWidth = indentOptions.TAB_SIZE; - return processViaPsi(node, range, new TreeHelperImpl(), useTabs, smartTabs, tabWidth); - } - - @NotNull - public static TextRange processViaPsi(@NotNull ASTNode node, - @NotNull TextRange range, - @NotNull TreeHelper treeHelper, - boolean useTabs, - boolean smartTabs, - int tabWidth) - { - AstHelper helper = new AstHelper(node, treeHelper); - do { - if (useTabs) { - if (smartTabs) { - range = processSmartTabs(helper, range, tabWidth); - } - else { - range = processTabs(helper, range, tabWidth); - } - } - else { - range = processSpaces(helper, range, tabWidth); - } - } - while (helper.nextLine()); - return range; - } - - @NotNull - public static TextRange processViaDocument(@NotNull Document document, - @NotNull TextRange range, - boolean useTabs, - boolean useSmartTabs, - int tabWidth) - { - TextRange result = range; - int startLine = document.getLineNumber(Math.min(document.getTextLength(), range.getStartOffset())); - int endLine = document.getLineNumber(Math.max(0, Math.min(document.getTextLength(), range.getEndOffset()) - 1)); - DocumentHelper helper = new DocumentHelper(document, startLine); - for (int line = startLine; line <= endLine; line++) { - helper.setLine(line); - if (useTabs) { - if (useSmartTabs) { - result = processSmartTabs(helper, result, tabWidth); - } - else { - result = processTabs(helper, result, tabWidth); - } - } - else { - result = processSpaces(helper, result, tabWidth); - } - } - return result; - } - - /** - * Converts tabulations to white spaces at the target line's indent space. - * - * @param helper data facade - * @param range target range allowed for modification - * @param tabWidth tab width in columns to use during conversion (each tab symbol is replaced by white spaces which number is - * equal to tab width) - * @return given text range if no modification to the target line's indent space has been performed: - * adjusted range that points to semantically the same region otherwise - */ - @NotNull - private static TextRange processSpaces(@NotNull Helper helper, @NotNull TextRange range, int tabWidth) { - CharSequence indent = helper.getCurrentLineIndent(); - int start = Math.max(0, range.getStartOffset() - helper.getCurrentLineStartOffset()); - int end = Math.min(indent.length(), range.getEndOffset() - helper.getCurrentLineStartOffset()); - int tabsNumber = 0; - int indentOffset = end; - for (int i = start; i < end; i++) { - char c = indent.charAt(i); - if (c == '\t') { - tabsNumber++; - } - else if (c != ' ') { - indentOffset = i; - break; - } - } - if (tabsNumber > 0) { - helper.replace(start, indentOffset, StringUtil.repeat(" ", indentOffset - start - tabsNumber + tabsNumber * tabWidth)); - return TextRange.create(range.getStartOffset(), range.getEndOffset() - tabsNumber + tabsNumber * tabWidth); - } - else { - return range; - } - } - - /** - * Converts white spaces to tabulations at the target line's indent space. - * - * @param helper data facade - * @param range target range allowed for modification - * @param tabWidth tab width in columns to use during conversion (each tab symbol is replaced by white spaces which number is - * equal to tab width) - * @return given text range if no modification to the target line's indent space has been performed: - * adjusted range that points to semantically the same region otherwise - */ - @NotNull - private static TextRange processTabs(@NotNull Helper helper, @NotNull TextRange range, int tabWidth) { - CharSequence indent = helper.getCurrentLineIndent(); - int start = Math.max(0, range.getStartOffset() - helper.getCurrentLineStartOffset()); - int end = Math.min(indent.length(), range.getEndOffset() - helper.getCurrentLineStartOffset()); - int replacementsNumber = 0; - int consecutiveSpaces = 0; - for (int i = start; i < end; i++) { - char c = indent.charAt(i); - if (c == ' ') { - ++consecutiveSpaces; - } - else { - int tabsNumber = consecutiveSpaces / tabWidth; - if (tabsNumber > 0) { - helper.replace(i - consecutiveSpaces, i - consecutiveSpaces + tabsNumber * tabWidth, StringUtil.repeat("\t", tabsNumber)); - replacementsNumber++; - consecutiveSpaces = 0; - } - if (c != '\t') { - break; - } - } - } - - int tabsNumber = consecutiveSpaces / tabWidth; - if (tabsNumber > 0) { - helper.replace(end - consecutiveSpaces, end - consecutiveSpaces + tabsNumber * tabWidth, StringUtil.repeat("\t", tabsNumber)); - } - - if (replacementsNumber > 0) { - return TextRange.create(range.getStartOffset(), range.getEndOffset() - replacementsNumber * (tabWidth - 1)); - } - else { - return range; - } - } - - /** - * Converts tabulations to white spaces at the target line's indent space. - * - * @param helper data facade - * @param range target range allowed for modification - * @param tabWidth tab width in columns to use during conversion (every group of 'tab width' white spaces from the indent space might - * be replaced by a tab symbol) - * @return given text range if no modification to the target line's indent space has been performed: - * adjusted range that points to semantically the same region otherwise - */ - @SuppressWarnings("AssignmentToForLoopParameter") - @NotNull - private static TextRange processSmartTabs(@NotNull Helper helper, @NotNull TextRange range, int tabWidth) { - // Adjust current line indent. The general idea is to replace white spaces by tab symbols if that maps to the previous line indent. - CharSequence prevLineIndent = helper.getPrevLineIndent(); - if (prevLineIndent == null) { - return processTabs(helper, range, tabWidth); - } - - CharSequence currentLineIndent = helper.getCurrentLineIndent(); - int lineStart = 0; - int start = Math.max(0, range.getStartOffset() - helper.getCurrentLineStartOffset()); - int end = Math.min(currentLineIndent.length(), range.getEndOffset() - helper.getCurrentLineStartOffset()); - int indentOffset = 0; - int tabsReplaced = 0; - for (int i = lineStart; i < end && indentOffset < prevLineIndent.length(); i++, indentOffset++) { - char c = currentLineIndent.charAt(i); - if (prevLineIndent.charAt(indentOffset) == ' ') { - if (c == ' ') { - continue; - } - else { - break; - } - } - - // Assuming that target prevLineIndent symbol is tab then. - if (c == '\t') { - continue; - } - - if (end - i < tabWidth) { - break; - } - - boolean canReplace = true; - for (int j = i + 1, max = Math.min(end, i + tabWidth); j < max; j++) { - if (currentLineIndent.charAt(j) != ' ') { - canReplace = false; - break; - } - } - - if (!canReplace) { - break; - } - - if (i < start) { - // Continue processing if target range doesn't cover the whole white spaces which are intended to replace tab symbol. - i += tabWidth - 1; // -1 because of 'for' loop increment - continue; - } - - helper.replace(i, i + tabWidth, "\t"); - tabsReplaced++; - end -= tabWidth - 1; - } - - return tabsReplaced > 0 ? TextRange.create(range.getStartOffset(), range.getEndOffset() - tabsReplaced * (tabWidth - 1)) : range; - } - - /** - * There are two possible processing use-cases: - *

-   * 
    - *
  • document-based processing;
  • - *
  • PSI-based processing;
  • - *
- *
- * That's why we hide implementation-specific processing behind the current interface and use it at the generic 'engine'. - *

- * The general idea is to process indent spaces line-by-line from top to bottom. - */ - interface Helper { - - /** - * @return previous line indent space if current line is not the first one; {@code null} otherwise - */ - @Nullable CharSequence getPrevLineIndent(); - - int getCurrentLineStartOffset(); - - /** @return current line's indent space */ - @NotNull CharSequence getCurrentLineIndent(); - - /** - * Asks current helper to modify target line's indent space. - * - * @param start start offset of the indent range to modify (counts from the line start, i.e. doesn't take into - * consideration line start offset at the document) - * @param end end offset of the indent range to modify (counts from the line start, i.e. doesn't take into - * consideration line start offset at the document) - * @param newText replacement text - */ - void replace(int start, int end, @NotNull String newText); - } - - private static class DocumentHelper implements Helper { - - @NotNull private final Document myDocument; - private int myLine; - private int myLineStartOffset; - - DocumentHelper(@NotNull Document document, int line) { - myDocument = document; - setLine(line); - } - - @Nullable - @Override - public CharSequence getPrevLineIndent() { - if (myLine <= 0) { - return null; - } - int prevLineStart = myDocument.getLineStartOffset(myLine - 1); - int prevLineIndentEnd = prevLineStart; - int prevLineEnd = myDocument.getLineEndOffset(myLine - 1); - CharSequence text = myDocument.getCharsSequence(); - for (; prevLineIndentEnd < prevLineEnd; prevLineIndentEnd++) { - char c = text.charAt(prevLineIndentEnd); - if (c != '\t' && c != ' ') { - break; - } - } - return text.subSequence(prevLineStart, prevLineIndentEnd); - } - - @Override - public int getCurrentLineStartOffset() { - return myLineStartOffset; - } - - @NotNull - @Override - public CharSequence getCurrentLineIndent() { - int end = myDocument.getLineEndOffset(myLine); - CharSequence text = myDocument.getCharsSequence(); - for (int i = myLineStartOffset; i < end; i++) { - char c = text.charAt(i); - if (c != ' ' && c != '\t') { - return text.subSequence(myLineStartOffset, i); - } - } - return text.subSequence(myLineStartOffset, end); - } - - @Override - public void replace(int start, int end, @NotNull String newText) { - myDocument.replaceString(myLineStartOffset + start, myLineStartOffset + end, newText); - } - - public void setLine(int line) { - myLine = line; - myLineStartOffset = myDocument.getLineStartOffset(line); - } - } - - private static class AstHelper implements Helper { - - @NotNull private final TreeHelper myHelper; - @Nullable private ASTNode myCurrentIndentHolder; - - private int myLineStartOffset; - - AstHelper(@NotNull ASTNode startNode, @NotNull TreeHelper helper) { - myHelper = helper; - myCurrentIndentHolder = myHelper.firstLeaf(startNode); - if (startNode.getStartOffset() <= 0) { - return; - } - nextLine(); - } - - @SuppressWarnings("LoopStatementThatDoesntLoop") - @Override - public CharSequence getPrevLineIndent() { - if (myCurrentIndentHolder == null) { - return null; - } - - // Check if current white space is multiline. - int end = myLineStartOffset - 1; - CharSequence text = myCurrentIndentHolder.getChars(); - for (int i = end - 1; i >= 0; i--) { - if (text.charAt(i) == '\n') { - return text.subSequence(i + 1, end); - } - } - for (ASTNode prev = prevIndentNode(myCurrentIndentHolder); prev != null; prev = prevIndentNode(prev)) { - CharSequence chars = prev.getChars(); - for (int i = chars.length() - 1; i >= 0; i--) { - if (chars.charAt(i) == '\n') { - return chars.subSequence(i + 1, chars.length()); - } - } - return chars; - } - return null; - } - - @Override - public int getCurrentLineStartOffset() { - ASTNode whiteSpace = myCurrentIndentHolder; - return whiteSpace == null ? 0 : whiteSpace.getStartOffset() + myLineStartOffset; - } - - @SuppressWarnings("UnusedAssignment") - @NotNull - @Override - public CharSequence getCurrentLineIndent() { - if (myCurrentIndentHolder == null || myLineStartOffset < 0) { - return ""; - } - - CharSequence text = myCurrentIndentHolder.getChars(); - for (int i = myLineStartOffset; i < text.length(); i++) { - char c = text.charAt(i); - if (c == '\n' || (c != ' ' && c != '\t')) { - return text.subSequence(myLineStartOffset, i); - } - } - return text.subSequence(myLineStartOffset, text.length()); - } - - @Override - public void replace(int start, int end, @NotNull String newText) { - if (myCurrentIndentHolder != null) { - myHelper.replace(newText, TextRange.create(start, end).shiftRight(getCurrentLineStartOffset()), myCurrentIndentHolder); - } - } - - public boolean nextLine() { - if (myCurrentIndentHolder == null) { - return false; - } - for (ASTNode node = myHelper.nextLeaf(myCurrentIndentHolder); node != null; node = myHelper.nextLeaf(node)) { - if (myCurrentIndentHolder.getTextLength() <= 0) { - continue; - } - CharSequence text = node.getChars(); - for (myLineStartOffset = 0; myLineStartOffset < text.length(); myLineStartOffset++) { - char c = text.charAt(myLineStartOffset); - if (c == '\n' && myLineStartOffset < text.length() - 1) { - myCurrentIndentHolder = node; - myLineStartOffset++; - return true; - } - } - } - - myCurrentIndentHolder = null; - return false; - } - - @Nullable - private ASTNode prevIndentNode(@NotNull ASTNode current) { - for (ASTNode candidate = myHelper.prevLeaf(current); candidate != null; candidate = myHelper.prevLeaf(candidate)) { - if (candidate.getStartOffset() <= 0 || StringUtil.contains(candidate.getChars(), 0, candidate.getTextLength(), '\n')) { - return candidate; - } - } - return null; - } - } - - public interface TreeHelper { - @Nullable - ASTNode prevLeaf(@NotNull ASTNode current); - - @Nullable - ASTNode nextLeaf(@NotNull ASTNode current); - - @Nullable - ASTNode firstLeaf(@NotNull ASTNode startNode); - - void replace(@NotNull String newText, @NotNull TextRange range, @NotNull ASTNode leaf); - } - - private static class TreeHelperImpl implements TreeHelper { - - @Override - public ASTNode prevLeaf(@NotNull ASTNode current) { - return TreeUtil.prevLeaf(current); - } - - @Nullable - @Override - public ASTNode nextLeaf(@NotNull ASTNode current) { - return TreeUtil.nextLeaf(current); - } - - @Nullable - @Override - public ASTNode firstLeaf(@NotNull ASTNode startNode) { - return TreeUtil.findFirstLeaf(startNode); - } - - @Override - public void replace(@NotNull String newText, @NotNull TextRange range, @NotNull ASTNode leaf) { - FormatterUtil.replaceInnerWhiteSpace(newText, leaf, range); - } - } -} diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java index 67629f036bce..00ba95c77149 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java @@ -18,9 +18,11 @@ package com.intellij.psi.impl.source.resolve.reference.impl; import com.intellij.codeInsight.completion.InsertHandler; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.codeInspection.reflectiveAccess.JavaReflectionInvocationInspection; +import com.intellij.codeInspection.reflectiveAccess.JavaReflectionMemberAccessInspection; import com.intellij.psi.*; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; @@ -28,10 +30,7 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Objects; -import java.util.Set; +import java.util.*; import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*; @@ -73,17 +72,29 @@ public class JavaLangClassMemberReference extends PsiReferenceBase isRegularMethod(method) && isPublic(method)); - if (publicMethod != null) { - return publicMethod; + PsiMethod[] methods = psiClass.findMethodsByName(name, true); + if (methods.length > 1) { + methods = + ContainerUtil.filter(methods, method -> isRegularMethod(method) && isPublic(method)) + .toArray(PsiMethod.EMPTY_ARRAY); + if (methods.length > 1) { + return findOverloadedMethod(methods); + } } - return ContainerUtil.find(methods, method -> isRegularMethod(method)); + return methods.length != 0 ? methods[0] : null; } case GET_DECLARED_METHOD: { - final PsiMethod[] methods = psiClass.findMethodsByName(name, false); - return ContainerUtil.find(methods, method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass)); + PsiMethod[] methods = psiClass.findMethodsByName(name, false); + if (methods.length > 1) { + methods = + ContainerUtil.filter(methods, method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass)) + .toArray(PsiMethod.EMPTY_ARRAY); + if (methods.length > 1) { + return findOverloadedMethod(methods); + } + } + return methods.length != 0 ? methods[0] : null; } } } @@ -155,6 +166,18 @@ public class JavaLangClassMemberReference extends PsiReferenceBase parameterTypes = JavaReflectionInvocationInspection.getReflectionMethodParameterTypes(definitionCall, 1); + if (parameterTypes != null) { + return JavaReflectionMemberAccessInspection.matchMethod(methods, parameterTypes); + } + } + return null; + } + @Override public void handleInsert(InsertionContext context, LookupElement item) { final Object object = item.getObject(); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java index 838ee3abe01e..2574d768a80e 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java @@ -19,13 +19,16 @@ import com.intellij.codeInsight.completion.InsertHandler; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.completion.JavaLookupElementBuilder; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; +import com.siyeh.ig.psiutils.ParenthesesUtils; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -97,9 +100,25 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase filter) { - final PsiMethod[] methods = psiClass.findMethodsByName(name, true); - return ContainerUtil.find(methods, filter); + private PsiElement resolveMethod(@NotNull String name, @NotNull PsiClass psiClass, Condition filter) { + PsiMethod[] methods = psiClass.findMethodsByName(name, true); + if (methods.length != 0) { + methods = ContainerUtil.filter(methods, filter).toArray(PsiMethod.EMPTY_ARRAY); + if (methods.length > 1) { + final PsiMethodCallExpression definitionCall = PsiTreeUtil.getParentOfType(myElement, PsiMethodCallExpression.class); + if (definitionCall != null) { + final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions(); + if (arguments.length > 2) { + final PsiExpression typeExpression = ParenthesesUtils.stripParentheses(arguments[2]); + final ReflectiveSignature expectedSignature = JavaLangInvokeHandleSignatureInspection.composeMethodSignature(typeExpression); + if (expectedSignature != null) { + return ContainerUtil.find(methods, method -> expectedSignature.equals(getMethodSignature(method))); + } + } + } + } + } + return methods.length != 0 ? methods[0] : null; } @NotNull diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationProcessor.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationProcessor.java index 9ddcf9b03aa8..c48fb4c9558b 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationProcessor.java @@ -16,7 +16,6 @@ package com.intellij.refactoring.typeMigration; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; @@ -35,12 +34,18 @@ import com.intellij.ui.content.Content; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewDescriptor; import com.intellij.usageView.UsageViewManager; -import com.intellij.util.*; -import com.intellij.util.containers.*; +import com.intellij.util.Function; +import com.intellij.util.Functions; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; import static com.intellij.util.ObjectUtils.assertNotNull; @@ -137,7 +142,7 @@ public class TypeMigrationProcessor extends BaseRefactoringProcessor { protected boolean preprocessUsages(@NotNull Ref refUsages) { if (hasFailedConversions()) { if (ApplicationManager.getApplication().isUnitTestMode()) { - throw new RuntimeException(StringUtil.join(myLabeler.getFailedConversionsReport(), "\n")); + throw new BaseRefactoringProcessor.ConflictsInTestsException(Arrays.asList(myLabeler.getFailedConversionsReport())); } FailedConversionsDialog dialog = new FailedConversionsDialog(myLabeler.getFailedConversionsReport(), myProject); if (!dialog.showAndGet()) { diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java index b0ee0debbe61..a1ff985f7a1e 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -34,6 +34,7 @@ import java.util.*; */ public abstract class NullableNotNullManager { protected static final Logger LOG = Logger.getInstance(NullableNotNullManager.class); + protected final Project myProject; public String myDefaultNullable = AnnotationUtil.NULLABLE; public String myDefaultNotNull = AnnotationUtil.NOT_NULL; @@ -41,7 +42,7 @@ public abstract class NullableNotNullManager { public final JDOMExternalizableStringList myNotNulls = new JDOMExternalizableStringList(); private static final String JAVAX_ANNOTATION_NULLABLE = "javax.annotation.Nullable"; - private static final String JAVAX_ANNOTATION_NONNULL = "javax.annotation.Nonnull"; + protected static final String JAVAX_ANNOTATION_NONNULL = "javax.annotation.Nonnull"; static final String[] DEFAULT_NULLABLES = {AnnotationUtil.NULLABLE, JAVAX_ANNOTATION_NULLABLE, "javax.annotation.CheckForNull", @@ -49,7 +50,8 @@ public abstract class NullableNotNullManager { "edu.umd.cs.findbugs.annotations.Nullable", "android.support.annotation.Nullable" }; - public NullableNotNullManager() { + public NullableNotNullManager(Project project) { + myProject = project; Collections.addAll(myNullables, DEFAULT_NULLABLES); } @@ -202,7 +204,7 @@ public abstract class NullableNotNullManager { String qName = annotation.getQualifiedName(); if (qName == null) return null; - List contradictory = nullable ? getNotNulls() : getNullables(); + List contradictory = nullable ? getNotNullsWithNickNames() : getNullablesWithNickNames(); if (contradictory.contains(qName)) return null; return annotation; @@ -241,13 +243,24 @@ public abstract class NullableNotNullManager { } private PsiAnnotation findPlainNullabilityAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) { - Set qNames = ContainerUtil.newHashSet(getNullables()); - qNames.addAll(getNotNulls()); + Set qNames = ContainerUtil.newHashSet(getNullablesWithNickNames()); + qNames.addAll(getNotNullsWithNickNames()); return checkBases && owner instanceof PsiMethod ? AnnotationUtil.findAnnotationInHierarchy(owner, qNames) : AnnotationUtil.findAnnotation(owner, qNames); } + + @NotNull + protected List getNullablesWithNickNames() { + return getNullables(); + } + + @NotNull + protected List getNotNullsWithNickNames() { + return getNotNulls(); + } + protected boolean hasHardcodedContracts(PsiElement element) { return false; } @@ -360,10 +373,10 @@ public abstract class NullableNotNullManager { public abstract List getPredefinedNotNulls(); public static boolean isNullableAnnotation(@NotNull PsiAnnotation annotation) { - return getInstance(annotation.getProject()).getNullables().contains(annotation.getQualifiedName()); + return getInstance(annotation.getProject()).getNullablesWithNickNames().contains(annotation.getQualifiedName()); } public static boolean isNotNullAnnotation(@NotNull PsiAnnotation annotation) { - return getInstance(annotation.getProject()).getNotNulls().contains(annotation.getQualifiedName()); + return getInstance(annotation.getProject()).getNotNullsWithNickNames().contains(annotation.getQualifiedName()); } } \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiConcatenationUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiConcatenationUtil.java index c7fcf5c8e122..ad5b90e58150 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiConcatenationUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiConcatenationUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -18,7 +18,7 @@ package com.intellij.psi.util; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -102,8 +102,8 @@ public class PsiConcatenationUtil { formatParameters.add(getBoxedArgument(expression)); } - private static PsiExpression getBoxedArgument(PsiExpression arg) throws IncorrectOperationException { - arg = PsiUtil.deparenthesizeExpression(arg); + private static PsiExpression getBoxedArgument(PsiExpression arg) { + arg = unwrapExpression(arg); assert arg != null; if (PsiUtil.isLanguageLevel5OrHigher(arg)) { return arg; @@ -130,4 +130,35 @@ public class PsiConcatenationUtil { return newExpr; } + @Nullable + private static PsiExpression unwrapExpression(PsiExpression expression) { + while (true) { + if (expression instanceof PsiParenthesizedExpression) { + expression = ((PsiParenthesizedExpression)expression).getExpression(); + continue; + } + if (expression instanceof PsiTypeCastExpression) { + final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)expression; + final PsiType castType = typeCastExpression.getType(); + if (TypeConversionUtil.isNumericType(castType)) { + final PsiExpression operand = typeCastExpression.getOperand(); + if (operand == null) { + return expression; + } + final PsiType operandType = operand.getType(); + if (operandType == null) { + return expression; + } + final int castRank = TypeConversionUtil.getTypeRank(castType); + final int operandRank = TypeConversionUtil.getTypeRank(operandType); + if (castRank < operandRank || castRank == TypeConversionUtil.CHAR_RANK && operandRank != castRank) { + return expression; + } + } + expression = typeCastExpression.getOperand(); + continue; + } + return expression; + } + } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index ba58f4e48a3d..1addc14dfc38 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -1984,8 +1984,8 @@ public class InferenceSession { final PsiSubstitutor sSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(gClass, (PsiClassType)sBound); final PsiSubstitutor tSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(gClass, (PsiClassType)tBound); for (PsiTypeParameter typeParameter : gClass.getTypeParameters()) { - final PsiType sType = sSubstitutor.substitute(typeParameter); - final PsiType tType = tSubstitutor.substitute(typeParameter); + final PsiType sType = sSubstitutor.substituteWithBoundsPromotion(typeParameter); + final PsiType tType = tSubstitutor.substituteWithBoundsPromotion(typeParameter); final Pair typePair = Pair.create(sType, tType); if (!processor.process(typePair)) { return gClass; diff --git a/java/java-tests/testData/codeInsight/createSwitch/notAvailableInForUpdate.java b/java/java-tests/testData/codeInsight/createSwitch/notAvailableInForUpdate.java new file mode 100644 index 000000000000..f7d6fab86901 --- /dev/null +++ b/java/java-tests/testData/codeInsight/createSwitch/notAvailableInForUpdate.java @@ -0,0 +1,5 @@ +class Test { + void m(int[] array) { + for (int i = 1; i < array.length; i++) { } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IncompatibleReturnTypeBounds.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IncompatibleReturnTypeBounds.java new file mode 100644 index 000000000000..9e44426fec3b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IncompatibleReturnTypeBounds.java @@ -0,0 +1,10 @@ +class ListA { + public Ta foo() { throw new Error(); } +} + +class ListB extends ListA { + public Rb foo() { throw new Error(); } +} + +class ListC extends ListB { +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference.java new file mode 100644 index 000000000000..03b94a1daa6b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference.java @@ -0,0 +1,5 @@ +class A { + { + Runnable r = Syst::setOu + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference_after.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference_after.java new file mode 100644 index 000000000000..eb1f75bd4b66 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/ChainedMethodReference_after.java @@ -0,0 +1,5 @@ +class A { + { + Runnable r = System::setOut; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/ValidIntersectionTypeWithCapturedBounds.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/ValidIntersectionTypeWithCapturedBounds.java new file mode 100644 index 000000000000..d66c2f810a19 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/ValidIntersectionTypeWithCapturedBounds.java @@ -0,0 +1,11 @@ + +abstract class Bug { + { + D _m = m(); + } + + abstract > J m(); +} + +abstract class C { } +abstract class D extends C { } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java index 5fc439fd75e8..0a030b150e1e 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java @@ -81,3 +81,27 @@ class InsideThisRxpression { public interface Bar extends Foo {} } +class SameDefaultMethodDifferentInheritors { + interface A { default void a() {} } + interface B extends A { default void a() {} } + interface B1 extends A { } + interface C extends A {} + + class Clazz implements B, C { + { + C.super.a(); + } + } + + class Clazz1 implements B1, C { + { + C.super.a(); + } + } + + class Clazz2 implements C { + { + C.super.a(); + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeAlreadyAssigned.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeAlreadyAssigned.java new file mode 100644 index 000000000000..94684be58489 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeAlreadyAssigned.java @@ -0,0 +1,10 @@ +// "Assign Parameter to Field 'myA'" "false" + +class Person { + int myA; + int myId; + void f(int a, String id) { + this.myA = foo(a); + } + int foo(int a) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeCheckAssignability.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeCheckAssignability.java new file mode 100644 index 000000000000..38db6f1a210d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforeCheckAssignability.java @@ -0,0 +1,10 @@ +// "Assign Parameter to Field 'myId'" "false" + +class Person { + int a; + int myId; + void f(int a, String id) { + this.a = foo(a); + } + int foo(int a) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforePassedToThisCall.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforePassedToThisCall.java new file mode 100644 index 000000000000..586a4ed8bc53 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/assignFieldFromParameter/beforePassedToThisCall.java @@ -0,0 +1,13 @@ +// "Assign Parameter to Field 'myStr'" "false" + + +class Foo1 { + final String myStr; + Foo1(String str, int i) { + myStr = (str); + } + + Foo1(String str) { + this(str, 2); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithArray/beforeRejectNullType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithArray/beforeRejectNullType.java new file mode 100644 index 000000000000..6972290707e4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithArray/beforeRejectNullType.java @@ -0,0 +1,9 @@ +// "Surround with array initialization" "false" +class A { + void m1(String[] s, + String[] s2, + String[] s3) {} + { + m1( null, null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBox.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBox.java similarity index 74% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBox.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBox.java index 3009f33c0c90..bfdff778c0b1 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBox.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBox.java @@ -1,4 +1,4 @@ -// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true" +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" public class Test { public void test(int a, int b) { if(Integer.compare(a, b) > 0) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBoxComplex.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBoxComplex.java similarity index 75% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBoxComplex.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBoxComplex.java index d981e9ef5252..f6d7b98cdf37 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/afterBoxComplex.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterBoxComplex.java @@ -1,4 +1,4 @@ -// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true" +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" public class Test { public int test(String s1, String s2) { int res = Integer.compare(s1.length(), s2.length()); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfAssign.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfAssign.java new file mode 100644 index 000000000000..019c86d4c1cc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfAssign.java @@ -0,0 +1,16 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +class Test { + public void test(String s1, String s2) { + int res; + res = Integer.compare(s2.length(), s1.length()) + System.out.println(res); + } + + public void testMissingElse(String s1, String s2) { + int res; + if(s1.length() < s2.length()) res = 1; + else if(s1.length() > s2.length()) res = -1; + res = 0; + System.out.println(res); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfReturn.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfReturn.java new file mode 100644 index 000000000000..aebbc5943de9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterIfReturn.java @@ -0,0 +1,17 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +class Test { + public int test(String s1, String s2) { + return Integer.compare(s1.length(), s2.length()); + /*otherwise bigger*/ + } + + public int test2(String s1, String s2) { + return Integer.compare(s2.length(), s1.length()); + } + + public int test3(String s1, String s2) { + if(s1.length() > s2.length()) return -1; + else if(s2.length() > s1.length()) return -1; + else return 0; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterTernary.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterTernary.java new file mode 100644 index 000000000000..b61ee932ddd3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/afterTernary.java @@ -0,0 +1,18 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +public class Test { + public void test(String s1, String s2) { + System.out.println(Integer.compare(s1.length(), s2.length())); + System.out.println(Integer.compare(s2.length(), s1.length())); + /*greater!*/ + /*less!*/ + /*equal!*/ + System.out.println(Integer.compare(s1.length(), s2.length())); + System.out.println(Integer.compare(s2.length(), s1.length())); + System.out.println(Integer.compare(s2.length(), s1.length())); + + System.out.println(s1.length() < s2.length() ? -1 : s1.length() == s2.length() ? 0 : 2); + System.out.println(s1.length() < s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1); + System.out.println(s1.length() == s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1); + System.out.println(s1.length() == s2.length() ? 0 : s2.length() < s2.length() ? -1 : 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBox.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBox.java similarity index 77% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBox.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBox.java index 98598424573b..d36be483d032 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBox.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBox.java @@ -1,4 +1,4 @@ -// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true" +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" public class Test { public void test(int a, int b) { if(((Integer)a).compareTo(b) > 0) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBoxComplex.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBoxComplex.java similarity index 75% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBoxComplex.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBoxComplex.java index d64b01c4bc03..213640090c52 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison/beforeBoxComplex.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeBoxComplex.java @@ -1,4 +1,4 @@ -// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true" +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" public class Test { public int test(String s1, String s2) { int res = new Integer(s1.length()).compareTo(s2.length()); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfAssign.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfAssign.java new file mode 100644 index 000000000000..c13a2b6af582 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfAssign.java @@ -0,0 +1,18 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +class Test { + public void test(String s1, String s2) { + int res; + if(s1.length() < s2.length()) res = 1; + else if(s1.length() > s2.length()) res = -1; + else res = 0; + System.out.println(res); + } + + public void testMissingElse(String s1, String s2) { + int res; + if(s1.length() < s2.length()) res = 1; + else if(s1.length() > s2.length()) res = -1; + res = 0; + System.out.println(res); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfReturn.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfReturn.java new file mode 100644 index 000000000000..fdd39ab32544 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeIfReturn.java @@ -0,0 +1,22 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +class Test { + public int test(String s1, String s2) { + if(s1.length() < s2.length()) { + return -1; + } + if((s1.length()) == s2.length()) return 0; + else /*otherwise bigger*/ return +1; + } + + public int test2(String s1, String s2) { + if(s1.length() > s2.length()) return -1; + else if(s2.length() > s1.length()) return 1; + else return 0; + } + + public int test3(String s1, String s2) { + if(s1.length() > s2.length()) return -1; + else if(s2.length() > s1.length()) return -1; + else return 0; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeTernary.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeTernary.java new file mode 100644 index 000000000000..e320398da6fb --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod/beforeTernary.java @@ -0,0 +1,15 @@ +// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true" +public class Test { + public void test(String s1, String s2) { + System.out.println(s1.length() < s2.length() ? -1 : s1.length() == s2.length() ? 0 : 1); + System.out.println((s1.length() > s2.length()) ? -1 : s1.length() == s2.length() ? 0 : 1); + System.out.println((s1.length() > s2.length()) ? /*greater!*/+1 : s1.length() < s2.length() ? /*less!*/-1 : /*equal!*/0); + System.out.println(s1.length() == s2.length() ? 0 : s2.length() < s1.length() ? -1 : 1); + System.out.println(s1.length() < s2.length() ? 1 : s2.length() < s1.length() ? -1 : 0); + + System.out.println(s1.length() < s2.length() ? -1 : s1.length() == s2.length() ? 0 : 2); + System.out.println(s1.length() < s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1); + System.out.println(s1.length() == s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1); + System.out.println(s1.length() == s2.length() ? 0 : s2.length() < s2.length() ? -1 : 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInspection/makeTypeGeneric/Conflict.java b/java/java-tests/testData/codeInspection/makeTypeGeneric/Conflict.java new file mode 100644 index 000000000000..405b5d3be415 --- /dev/null +++ b/java/java-tests/testData/codeInspection/makeTypeGeneric/Conflict.java @@ -0,0 +1,8 @@ +import java.util.*; + +public class F { + { + List list= new ArrayList(); + list.add(""); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java b/java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java new file mode 100644 index 000000000000..c2f20a40f1a3 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/TypeQualifierNickname.java @@ -0,0 +1,15 @@ +import javax.annotation.meta.*; + +@TypeQualifierNickname() +@javax.annotation.Nonnull(when = When.MAYBE) +@interface NullableNick {} + +interface UnknownInterface { + void foo(String s); +} + +class ImplWithNotNull implements UnknownInterface { + public void foo(@NullableNick String s) { + System.out.println(s.hashCode()); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/RegExpHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/RegExpHighlightingTest.java index 137b1c9c6b2c..a6165474f638 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/RegExpHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/RegExpHighlightingTest.java @@ -31,6 +31,10 @@ import org.jetbrains.annotations.NotNull; @SuppressWarnings("Annotator") public class RegExpHighlightingTest extends LightCodeInsightFixtureTestCase { + public void testDuplicateNamedGroup() { + doTest("(?abc)(?<name>xyz)"); + } + public void testAnonymousCapturingGroupInspection() { myFixture.enableInspections(new AnonymousGroupInspection()); doTest("(moo)\\1"); diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/Normal8CompletionTest.groovy b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/Normal8CompletionTest.groovy index 30a1fbd37628..ae0af6127049 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/Normal8CompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/Normal8CompletionTest.groovy @@ -212,37 +212,37 @@ class Test88 { void testCollectorsToList() { configureByTestName() selectItem(myItems.find { it.lookupString.contains('toList') }) - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testStaticallyImportedCollectorsToList() { configureByTestName() selectItem(myItems.find { it.lookupString.contains('collect(toList())') }) - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testAllCollectors() { configureByTestName() myFixture.assertPreferredCompletionItems 0, 'collect', 'collect', 'collect(Collectors.toCollection())', 'collect(Collectors.toList())', 'collect(Collectors.toSet())' selectItem(myItems.find { it.lookupString.contains('toCollection') }) - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testCollectorsToSet() { configureByTestName() selectItem(myItems.find { it.lookupString.contains('toSet') }) - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testNoExplicitTypeArgsInTernary() { configureByTestName() selectItem(myItems.find { it.lookupString.contains('empty') }) - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testCallBeforeLambda() { configureByTestName() - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testLambdaInAmbiguousCall() { @@ -264,13 +264,13 @@ class Test88 { void testNoSemicolonAfterVoidMethodInLambda() { configureByTestName() myFixture.type('l\t') - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testFinishMethodReferenceWithColon() { configureByTestName() myFixture.type(':') - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testPreferLocalsOverMethodRefs() { @@ -286,14 +286,22 @@ class Test88 { "}") configureByTestName() myFixture.type('\n') - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } void testOverrideMethodAsDefault() { configureByTestName() assert LookupElementPresentation.renderElement(myFixture.lookupElements[0]).itemText == 'default void run' myFixture.type('\t') - checkResultByFile(getTestName(false) + "_after.java") + checkResultByFileName() } + void testChainedMethodReference() { + configureByTestName() + checkResultByFileName() + } + + private checkResultByFileName() { + checkResultByFile(getTestName(false) + "_after.java") + } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/GenericsHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/GenericsHighlightingTest.java index 2f0480f4bce6..d2276ff4bc6e 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/GenericsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/GenericsHighlightingTest.java @@ -180,6 +180,7 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase { public void testInaccessibleThroughWildcard() { doTest7Incompatibility(false);} public void testInconvertibleTypes() { doTest5(false); } public void testIncompatibleReturnType() { doTest5(false); } + public void testContinueInferenceAfterFirstRawResult() { doTest5(false); } public void testDoNotAcceptLowerBoundIfRaw() { doTest5(false); } public void testStaticOverride() { doTest5(false); } @@ -342,6 +343,7 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase { public void testIDEA71582() { doTest5(false); } public void testIDEA65377() { doTest5(false); } public void testIDEA113526() { doTest5(true); } + public void testIncompatibleReturnTypeBounds() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA116493() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA117827() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA118037() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java index 2c09c1102be5..645a9442f600 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java @@ -115,6 +115,7 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase public void testPullUncheckedWarningNotionThroughNestedCalls() { doTest(); } public void testIDEA149774() { doTest(); } public void testDisjunctionTypes() { doTest(); } + public void testValidIntersectionTypeWithCapturedBounds() { doTest(); } public void testPushErasedStateToArguments() { doTest(); } public void testStopAtStandaloneConditional() { doTest(); } public void testTransitiveInferenceVariableDependencies() { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/BoxForComparisonInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/UseCompareMethodInspectionTest.java similarity index 82% rename from java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/BoxForComparisonInspectionTest.java rename to java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/UseCompareMethodInspectionTest.java index 6689194b0e3c..204121ac1613 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/BoxForComparisonInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/UseCompareMethodInspectionTest.java @@ -16,17 +16,17 @@ package com.intellij.java.codeInsight.daemon.quickFix; import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; -import com.intellij.codeInspection.BoxForComparisonInspection; import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.UseCompareMethodInspection; import org.jetbrains.annotations.NotNull; -public class BoxForComparisonInspectionTest extends LightQuickFixParameterizedTestCase { +public class UseCompareMethodInspectionTest extends LightQuickFixParameterizedTestCase { @NotNull @Override protected LocalInspectionTool[] configureLocalInspectionTools() { return new LocalInspectionTool[]{ - new BoxForComparisonInspection(), + new UseCompareMethodInspection(), }; } @@ -34,6 +34,6 @@ public class BoxForComparisonInspectionTest extends LightQuickFixParameterizedTe @Override protected String getBasePath() { - return "/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison"; + return "/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod"; } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/CreateSwitchTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/CreateSwitchTest.java index 286d1b717a53..e342f4c8692e 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/CreateSwitchTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/intention/CreateSwitchTest.java @@ -55,6 +55,10 @@ public class CreateSwitchTest extends JavaCodeInsightFixtureTestCase { doTestNotAvailable(); } + public void testNotAvailableInForUpdate() { + doTestNotAvailable(); + } + private void doTestString() { final LanguageLevelProjectExtension languageLevelProjectExtension = LanguageLevelProjectExtension.getInstance(getProject()); final LanguageLevel oldLanguageLevel = languageLevelProjectExtension.getLanguageLevel(); diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaLangInvokeHandleNavigationTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaLangInvokeHandleNavigationTest.kt index 73e7aa0e2f4c..b8f0b87796bb 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaLangInvokeHandleNavigationTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaLangInvokeHandleNavigationTest.kt @@ -16,10 +16,13 @@ package com.intellij.java.codeInsight.navigation import com.intellij.psi.PsiMember +import com.intellij.psi.PsiMethod import com.intellij.psi.PsiReference import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import junit.framework.TestCase +import org.intellij.lang.annotations.Language import org.intellij.lang.annotations.MagicConstant +import org.jetbrains.annotations.NonNls /** * @author Pavel.Dolgov @@ -74,6 +77,64 @@ class JavaLangInvokeHandleNavigationTest : LightCodeInsightFixtureTestCase() { fun testStaticSetter6() = doNegativeTest("pf1", STATIC_SETTER) fun testStaticSetter7() = doNegativeTest("m1", STATIC_SETTER) + fun testOverloadedBothPublic() = doTestOverloaded( + """public class Overloaded { + public void foo(int n) {} + public void foo(String s) {} +}""", VIRTUAL, "java.lang.String") + + fun testOverloadedFirstPublic() = doTestOverloaded( + """public class Overloaded { + public void foo(int n) {} + void foo(String s) {} +}""", VIRTUAL, "int") + + fun testOverloadedSecondPublic() = doTestOverloaded( + """public class Overloaded { + void foo(int n) {} + public void foo(String s) {} +}""", VIRTUAL, "java.lang.String") + + fun testOverloadedInherited() { + myFixture.addClass("""public class OverloadedParent { + public static void foo(String s) {} +}""") + + doTestOverloaded( + """public class Overloaded extends OverloadedParent { + public static void foo(int n) {} +}""", STATIC, "java.lang.String") + } + + fun testOverloadedStatic() = doTestOverloaded( + """public class Overloaded { + public static void foo(int n) {} + public static void foo(String s) {} +}""", STATIC, "java.lang.String") + + + private fun doTestOverloaded(@NonNls @Language("JAVA") classText: String, function: String, vararg expectedParameterTypes: String) { + myFixture.addClass(classText) + + val methodType = arrayOf("void", *expectedParameterTypes).map { "$it.class" }.joinToString(", ") + val member = doTestImpl("foo", """ +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +class Main { + void foo() throws ReflectiveOperationException { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.$function(Overloaded.class, "foo", MethodType.methodType($methodType)); + } +}""") + + TestCase.assertTrue("Is method", member is PsiMethod) + val parameters = (member as PsiMethod).parameterList.parameters + TestCase.assertEquals("Parameter count", expectedParameterTypes.size, parameters.size) + for (i in 0 until expectedParameterTypes.size) { + TestCase.assertEquals("Parameter $i", expectedParameterTypes[i], parameters[i].type.canonicalText) + } + } + private fun doTest(name: String, @MagicConstant(stringValues = arrayOf(VIRTUAL, STATIC, SPECIAL, @@ -83,7 +144,7 @@ class JavaLangInvokeHandleNavigationTest : LightCodeInsightFixtureTestCase() { doTestImpl(name, getMainClassText(name, function)) } - private fun doTestImpl(name: String, mainClassText: String) { + private fun doTestImpl(name: String, mainClassText: String): PsiMember { val reference = getReference(mainClassText) TestCase.assertEquals("Reference text", name, reference.canonicalText) val resolved = reference.resolve() @@ -91,6 +152,7 @@ class JavaLangInvokeHandleNavigationTest : LightCodeInsightFixtureTestCase() { TestCase.assertTrue("Target is a member", resolved is PsiMember) val member = resolved as PsiMember? TestCase.assertEquals("Target name", name, member!!.name) + return member } private fun doNegativeTest(name: String, diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaReflectionNavigationTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaReflectionNavigationTest.java index e904b9b9deb1..1102cb973937 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaReflectionNavigationTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/navigation/JavaReflectionNavigationTest.java @@ -15,9 +15,7 @@ */ package com.intellij.java.codeInsight.navigation; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiMember; -import com.intellij.psi.PsiReference; +import com.intellij.psi.*; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; import org.intellij.lang.annotations.Language; import org.intellij.lang.annotations.MagicConstant; @@ -93,6 +91,70 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas "}"); } + public void testOverloadedMethodBothPublic() { + doTestOverloadedMethod("foo", + "class Overloaded {\n" + + " public void foo() {}\n" + + " public void foo(String s) {}\n" + + "}", false, "java.lang.String"); + } + + public void testOverloadedMethodPublicFirst() { + doTestOverloadedMethod("foo", + "class Overloaded {\n" + + " public void foo() {}\n" + + " void foo(String s) {}\n" + + "}", false); + } + + public void testOverloadedMethodPublicSecond() { + doTestOverloadedMethod("foo", + "class Overloaded {\n" + + " void foo() {}\n" + + " public void foo(String s) {}\n" + + "}", false, "java.lang.String"); + } + + public void testOverloadedDeclaredMethod() { + doTestOverloadedMethod("foo", + "class Overloaded {\n" + + " public void foo() {}\n" + + " public void foo(String s) {}\n" + + "}", true, "java.lang.String"); + } + + public void testOverloadedInheritedMethod() { + doTestOverloadedMethod("bar", + "class OverloadedParent {" + + " public void bar(String s) {}\n" + + "}" + + "" + + "class Overloaded extends OverloadedParent {\n" + + " public void bar() {}\n" + + "}", false, "java.lang.String"); + } + + private void doTestOverloadedMethod(String name, + @NotNull @NonNls @Language("JAVA") String classText, + boolean isDeclared, + String... expectedParameterTypes) { + myFixture.addClass(classText); + + PsiMember member = doTestImpl(name, + "class Main {" + + " void main() {" + + " Overloaded.class.get" + (isDeclared?"Declared":"") + "Method(\""+name+"\", String.class);" + + " }" + + "}"); + assertTrue("Target is a method", member instanceof PsiMethod); + PsiMethod method = (PsiMethod)member; + PsiParameter[] parameters = method.getParameterList().getParameters(); + assertEquals("Parameter count", expectedParameterTypes.length, parameters.length); + for (int i = 0; i < expectedParameterTypes.length; i++) { + assertEquals("Parameter type " + i, expectedParameterTypes[i], parameters[0].getType().getCanonicalText()); + } + } + private void doTest(String name, @MagicConstant(stringValues = {FIELD, METHOD, DF, DM}) String type) { @@ -104,7 +166,7 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas doTestImpl(name, mainClassText); } - private void doTestImpl(String name, String mainClassText) { + private PsiMember doTestImpl(String name, @NotNull @NonNls @Language("JAVA") String mainClassText) { PsiReference reference = getReference(mainClassText); assertEquals("Reference text", name, reference.getCanonicalText()); PsiElement resolved = reference.resolve(); @@ -112,6 +174,7 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas assertTrue("Target is a member", resolved instanceof PsiMember); PsiMember member = (PsiMember)resolved; assertEquals("Target name", name, member.getName()); + return member; } private void doNegativeTest(String name, diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java index ee1d16172c03..062f0c1a91aa 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java @@ -360,6 +360,17 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { myFixture.enableInspections(inspection); myFixture.testHighlighting(true, false, true, getTestName(false) + ".java"); } + + public void testTypeQualifierNickname() { + addJavaxNullabilityAnnotations(myFixture); + + myFixture.addClass("package bar;" + + "import javax.annotation.meta.*;" + + "@TypeQualifierNickname() @javax.annotation.NonNull(when = Maybe.MAYBE) " + + "public @interface NullableNick {}"); + + doTest(); + } public static void addJavaxDefaultNullabilityAnnotations(final JavaCodeInsightTestFixture fixture) { fixture.addClass("package javax.annotation;" + @@ -371,12 +382,23 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { } public static void addJavaxNullabilityAnnotations(final JavaCodeInsightTestFixture fixture) { - fixture.addClass("package javax.annotation;" + - "public @interface Nonnull {}"); - fixture.addClass("package javax.annotation;" + - "public @interface Nullable {}"); fixture.addClass("package javax.annotation.meta;" + "public @interface TypeQualifierDefault { java.lang.annotation.ElementType[] value() default {};}"); + fixture.addClass("package javax.annotation.meta;" + + "public enum When { ALWAYS, UNKNOWN, MAYBE, NEVER }"); + fixture.addClass("package javax.annotation.meta;" + + "public @interface TypeQualifierNickname {}"); + + fixture.addClass("package javax.annotation;" + + "import javax.annotation.meta.*;" + + "public @interface Nonnull {" + + " When when() default When.ALWAYS;" + + "}"); + fixture.addClass("package javax.annotation;" + + "import javax.annotation.meta.*;" + + "@TypeQualifierNickname " + + "@Nonnull(when = When.UNKNOWN) " + + "public @interface Nullable {}"); } public void testCustomTypeQualifierDefault() { diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/RawTypeCanBeGenericTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/RawTypeCanBeGenericTest.java index dd4f63dcef20..2ba15e4ec490 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/RawTypeCanBeGenericTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/RawTypeCanBeGenericTest.java @@ -20,8 +20,11 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.miscGenerics.RawTypeCanBeGenericInspection; import com.intellij.openapi.roots.ModuleRootModificationUtil; +import com.intellij.refactoring.BaseRefactoringProcessor; import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; import java.util.List; @@ -63,6 +66,17 @@ public class RawTypeCanBeGenericTest extends LightCodeInsightFixtureTestCase { doTest(getMessage("list", "List")); } + public void testConflict() { + try { + doTest(getMessage("list", "List")); + fail("No conflict detected"); + } + catch (BaseRefactoringProcessor.ConflictsInTestsException e) { + assertEquals("Cannot convert type of expression "" from java.lang.String to T
", + e.getMessage()); + } + } + public void testAtInitializer() { assertIntentionNotAvailable(getMessagePrefix()); } @@ -92,4 +106,10 @@ public class RawTypeCanBeGenericTest extends LightCodeInsightFixtureTestCase { String message = InspectionsBundle.message("inspection.raw.variable.type.can.be.generic.quickfix", "@", "@"); return message.substring(0, message.indexOf("@")); } + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_1_6; + } } diff --git a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavadocFormatterTest.java b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavadocFormatterTest.java index 250b0a5e45ae..36b34e7b6c83 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavadocFormatterTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavadocFormatterTest.java @@ -986,6 +986,22 @@ public class JavadocFormatterTest extends AbstractJavaFormatterTest { ); } + public void test_JdWithTabs() { + doClassTest( + "\t/**\n" + + "\t \t *\n" + + "\t \t *\n" + + "\t \t */\n" + + "\tvoid check() {\n" + + "\t}", + "/**\n" + + " *\n" + + " *\n" + + " */\n" + + "void check() {\n" + + "}" + ); + } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/psi/impl/source/codeStyle/TabPostFormatProcessorTest.java b/java/java-tests/testSrc/com/intellij/java/psi/impl/source/codeStyle/TabPostFormatProcessorTest.java deleted file mode 100644 index c53d9e602385..000000000000 --- a/java/java-tests/testSrc/com/intellij/java/psi/impl/source/codeStyle/TabPostFormatProcessorTest.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright 2000-2017 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.java.psi.impl.source.codeStyle; - -import com.intellij.lang.ASTNode; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.impl.LineSet; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.TokenType; -import com.intellij.psi.impl.source.codeStyle.TabPostFormatProcessor; -import com.intellij.psi.tree.IElementType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jmock.Expectations; -import org.jmock.Mockery; -import org.jmock.api.Invocation; -import org.jmock.integration.junit4.JMock; -import org.jmock.integration.junit4.JUnit4Mockery; -import org.jmock.lib.action.CustomAction; -import org.jmock.lib.legacy.ClassImposteriser; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertEquals; - -/** - * @author Denis Zhdanov - * @since 08/01/2012 - */ -@RunWith(JMock.class) -public class TabPostFormatProcessorTest { - - private static final String START_RANGE_MARKER = ""; - private static final String END_RANGE_MARKER = ""; - - private Mockery myMockery; - private Document myDocument; - - @Before - public void setUp() { - myMockery = new JUnit4Mockery() {{ - setImposteriser(ClassImposteriser.INSTANCE); - }}; - myDocument = myMockery.mock(Document.class); - } - - @After - public void checkExpectations() { - myMockery.assertIsSatisfied(); - } - - @Test - public void spacesAndWholeLineInsideRange() { - doTestSpaces( - "line 1\n" + - " \t \tline2\n" + - "line 3", - 4, - "line 1\n" + - " line2\n" + - "line 3" - ); - } - @Test - public void spacesAndExactRange() { - doTestSpaces( - "line 1\n" + - " \t \tline2\n" + - "line 3", - 4, - "line 1\n" + - " line2\n" + - "line 3" - ); - } - - @Test - public void spacesAndHeadIntersection() { - doTestSpaces( - "line 1\n" + - " \t \tline2\n" + - "line 3", - 4, - "line 1\n" + - " \tline2\n" + - "line 3" - ); - } - - @Test - public void spacesAndTailIntersection() { - doTestSpaces( - "line 1\n" + - " \t \tline2\n" + - "line 3", - 4, - "line 1\n" + - " \t line2\n" + - "line 3" - ); - } - - @Test - public void spacesAndPartialIndentInsideRange() { - doTestSpaces( - "line 1\n" + - " \t \t \t line2\n" + - "line 3", - 4, - "line 1\n" + - " \t \t line2\n" + - "line 3" - ); - } - - @Test - public void tabsAndWholeLineInsideRange() { - doTestTabs( - "line 1\n" + - " \t line2\n" + - "line 3", - 4, - "line 1\n" + - "\t \t line2\n" + - "line 3" - ); - } - - @Test - public void tabsAndHeadIntersection() { - doTestTabs( - "line 1\n" + - " \t line2\n" + - "line 3", - 4, - "line 1\n" + - "\t\t line2\n" + - "line 3" - ); - } - - @Test - public void tabsAndTailIntersection() { - doTestTabs( - "line 1\n" + - " line2\n" + - "line 3", - 4, - "line 1\n" + - " \t line2\n" + - "line 3" - ); - } - - @Test - public void tabsAndPartialIndentInsideRange() { - doTestTabs( - "line 1\n" + - " line2\n" + - "line 3", - 4, - "line 1\n" + - " \t line2\n" + - "line 3" - ); - } - - @Test - public void smartTabsForTheFirstLine() { - doTestSmartTabs( - " line 1\n" + - "\t line 2", - 4, - "\t\t line 1\n" + - "\t line 2" - ); - } - - @Test - public void smartTabsFromUpperLine() { - doTestSmartTabs( - "\t\t line 1\n" + - " 2", - 4, - "\t\t line 1\n" + - "\t\t 2" - ); - } - - @Test - public void smartTabsExactReplacement() { - doTestSmartTabs( - "\tline 1\n" + - " line 2", - 4, - "\tline 1\n" + - "\tline 2" - ); - } - - @Test - public void smartTabsMismatchedIndent() { - doTestSmartTabs( - " \tline 1\n" + - "\t line 2", - 4, - " \tline 1\n" + - "\t line 2" - ); - } - - @Test - public void smartTabsPartialMatchedIndent() { - doTestSmartTabs( - "\t\tline 1\n" + - " line 2", - 4, - "\t\tline 1\n" + - " \tline 2" - ); - } - - @Test - public void smartTabsPartialMisMatchedIndent() { - doTestSmartTabs( - "\t\tline 1\n" + - " line 2", - 4, - "\t\tline 1\n" + - " line 2" - ); - } - - private void doTestSpaces(@NotNull String initial, final int tabWidth, @NotNull String expected) { - doTest(initial, expected, false, false, tabWidth); - } - - private void doTestTabs(@NotNull String initial, final int tabWidth, @NotNull String expected) { - doTest(initial, expected, true, false, tabWidth); - } - - private void doTestSmartTabs(@NotNull String initial, final int tabWidth, @NotNull String expected) { - doTest(initial, expected, true, true, tabWidth); - } - - private void doTest(@NotNull String initial, @NotNull String expected, boolean useTabs, boolean smartTabs, int tabWidth) { - doDocumentTest(initial, expected, useTabs, smartTabs, tabWidth); - doPsiTest(initial, expected, useTabs, smartTabs, tabWidth); - } - - private void doDocumentTest(@NotNull String initial, @NotNull String expected, boolean useTabs, boolean smartTabs, int tabWidth) { - Pair pair = parse(initial); - final StringBuilder text = new StringBuilder(pair.first); - final TextRange range = pair.second; - - myMockery.checking(new Expectations() {{ - allowing(myDocument).getCharsSequence(); will(returnValue(text.toString())); - allowing(myDocument).getTextLength(); will(returnValue(text.length())); - }}); - - final LineSet lines = LineSet.createLineSet(myDocument.getCharsSequence()); - myMockery.checking(new Expectations() {{ - allowing(myDocument).getLineNumber(with(any(int.class))); will(new CustomAction("getLineNumber()") { - @Override - public Object invoke(Invocation invocation) throws Throwable { - return lines.findLineIndex((Integer)invocation.getParameter(0)); - } - }); - allowing(myDocument).getLineStartOffset(with(any(int.class))); will(new CustomAction("getLineStartOffset()") { - @Override - public Object invoke(Invocation invocation) throws Throwable { - return lines.getLineStart((Integer)invocation.getParameter(0)); - } - }); - allowing(myDocument).getLineEndOffset(with(any(int.class))); will(new CustomAction("getLineEndOffset()") { - @Override - public Object invoke(Invocation invocation) throws Throwable { - return lines.getLineEnd((Integer)invocation.getParameter(0)); - } - }); - allowing(myDocument).replaceString(with(any(int.class)), with(any(int.class)), with(any(String.class))); - will(new CustomAction("replaceString") { - @Nullable - @Override - public Object invoke(Invocation invocation) throws Throwable { - int start = (Integer)invocation.getParameter(0); - int end = (Integer)invocation.getParameter(1); - String newText = (String)invocation.getParameter(2); - text.replace(start, end, newText); - return null; - } - }); - }}); - - TabPostFormatProcessor.processViaDocument(myDocument, range, useTabs, smartTabs, tabWidth); - assertEquals(expected, text.toString()); - } - - private static Pair parse(@NotNull String text) { - int rangeMarkerStart = text.indexOf(START_RANGE_MARKER); - int rangeMarkerEnd = text.indexOf(END_RANGE_MARKER); - final StringBuilder buffer = new StringBuilder(); - final TextRange range; - if (rangeMarkerStart >= 0 && rangeMarkerEnd >= 0) { - range = TextRange.create(rangeMarkerStart, rangeMarkerEnd - START_RANGE_MARKER.length()); - buffer.append(text.substring(0, rangeMarkerStart)) - .append(text.substring(rangeMarkerStart + START_RANGE_MARKER.length(), rangeMarkerEnd)) - .append(text.substring(rangeMarkerEnd + END_RANGE_MARKER.length())); - } - else { - range = TextRange.create(0, text.length()); - buffer.append(text); - } - return Pair.create(buffer.toString(), range); - } - - private void doPsiTest(@NotNull String initial, @NotNull String expected, boolean useTabs, boolean smartTabs, int tabWidth) { - final List children = new ArrayList<>(); - final List childrenText = new ArrayList<>(); - Pair pair = parse(initial); - final String text = pair.first; - int start = 0; - boolean inWhiteSpace = initial.charAt(0) == ' ' || initial.charAt(0) == '\t'; - for (int i = 1; i <= text.length(); i++) { - if (i == text.length() || ((StringUtil.isWhiteSpace(text.charAt(i))) ^ inWhiteSpace)) { - final int childIndex = children.size(); - final int startOffset = start; - childrenText.add(new StringBuilder(text.substring(start, i))); - final ASTNode child = myMockery.mock(ASTNode.class, "child" + childIndex); - children.add(child); - final IElementType type = inWhiteSpace ? TokenType.WHITE_SPACE : TokenType.CODE_FRAGMENT; - myMockery.checking(new Expectations() {{ - allowing(child).getElementType(); will(returnValue(type)); - allowing(child).getChars(); will(returnValue(childrenText.get(childIndex))); - allowing(child).getTextLength(); will(returnValue(childrenText.get(childIndex).length())); - allowing(child).getStartOffset(); will(returnValue(startOffset)); - }}); - inWhiteSpace = !inWhiteSpace; - start = i; - } - } - - final ASTNode root = myMockery.mock(ASTNode.class); - myMockery.checking(new Expectations() {{ - allowing(root).getFirstChildNode(); will(returnValue(children.get(0))); - allowing(root).getTextLength(); will(returnValue(text.length())); - allowing(root).getStartOffset(); will(returnValue(0)); - }}); - - TabPostFormatProcessor.TreeHelper helper = new TabPostFormatProcessor.TreeHelper() { - - @Override - public ASTNode prevLeaf(@NotNull ASTNode current) { - int i = children.indexOf(current); - return i > 0 ? children.get(i - 1) : null; - } - - @Override - public ASTNode nextLeaf(@NotNull ASTNode current) { - int i = children.indexOf(current); - return i < children.size() - 1 ? children.get(i + 1) : null; - } - - @Override - public ASTNode firstLeaf(@NotNull ASTNode startNode) { - return root == startNode ? children.get(0) : null; - } - - @Override - public void replace(@NotNull String newText, @NotNull TextRange range, @NotNull ASTNode leaf) { - int i = children.indexOf(leaf); - childrenText.get(i).replace(range.getStartOffset() - leaf.getStartOffset(), range.getEndOffset() - leaf.getStartOffset(), newText); - } - }; - - TabPostFormatProcessor.processViaPsi(root, pair.second, helper, useTabs, smartTabs, tabWidth); - StringBuilder actual = new StringBuilder(); - for (ASTNode child : children) { - actual.append(child.getChars()); - } - assertEquals(expected, actual.toString()); - } -} diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java index 6161a42af635..0e03d91dd617 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java @@ -115,7 +115,9 @@ public class ConvertFieldToThreadLocalIntention extends PsiElementBaseIntentionA } PsiExpression initializer = psiField.getInitializer(); - if (initializer == null) { + + if (initializer == null && + !psiField.hasModifierProperty(PsiModifier.FINAL)) { final PsiType type = psiField.getType(); String initializerText = null; if (PsiType.BOOLEAN.equals(type)) { diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java index 49047421571e..2acf82df2ac3 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java @@ -12,6 +12,8 @@ import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; import com.intellij.refactoring.typeMigration.TypeEvaluator; import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; +import com.intellij.util.ObjectUtils; +import com.siyeh.HardcodedMethodConstants; import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,15 +40,15 @@ public class AtomicConversionRule extends TypeConversionRule { } private static boolean isAtomicTypeMigration(PsiType from, PsiClassType to, PsiExpression context) { - if (PsiType.INT.equals(from) && to.getCanonicalText().equals(AtomicInteger.class.getName())) { + if (PsiType.INT.isAssignableFrom(from) && to.getCanonicalText().equals(AtomicInteger.class.getName())) { + return true; + } + if (PsiType.LONG.isAssignableFrom(from) && to.getCanonicalText().equals(AtomicLong.class.getName())) { return true; } if (from.equals(PsiType.INT.createArrayType()) && to.getCanonicalText().equals(AtomicIntegerArray.class.getName())) { return true; } - if (PsiType.LONG.equals(from) && to.getCanonicalText().equals(AtomicLong.class.getName())) { - return true; - } if (from.equals(PsiType.LONG.createArrayType()) && to.getCanonicalText().equals(AtomicLongArray.class.getName())) { return true; } @@ -342,6 +344,9 @@ public class AtomicConversionRule extends TypeConversionRule { if (context instanceof PsiArrayAccessExpression) { return new TypeConversionDescriptor("$qualifier$[$idx$]", "$qualifier$.get($idx$)", (PsiExpression)context); } + if (parent instanceof PsiReferenceExpression && isReferenceToLengthField((PsiReferenceExpression)parent)) { + return new TypeConversionDescriptor("$qualifier$.length", "$qualifier$.length()", (PsiExpression)parent); + } return null; } @@ -438,4 +443,13 @@ public class AtomicConversionRule extends TypeConversionRule { return null; } + private static boolean isReferenceToLengthField(@NotNull PsiReferenceExpression refExpr) { + if (!"length".equals(refExpr.getReferenceName())) { + return false; + } + PsiClass aClass = JavaPsiFacade.getElementFactory(refExpr.getProject()).getArrayClass(PsiUtil.getLanguageLevel(refExpr)); + PsiField lengthField = ObjectUtils.notNull(aClass.findFieldByName(HardcodedMethodConstants.LENGTH, false)); + return refExpr.isReferenceTo(lengthField); + } + } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java index e403dfb45e99..bf7e2cc746aa 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java @@ -1,5 +1,7 @@ package com.intellij.refactoring.typeMigration.rules; +import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; +import com.intellij.codeInsight.daemon.impl.quickfix.VariableAccessFromInnerClassFix; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.psi.*; @@ -8,10 +10,14 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; +import com.intellij.refactoring.typeMigration.TypeEvaluator; import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + public class ThreadLocalConversionRule extends TypeConversionRule { private static final Logger LOG = Logger.getInstance(ThreadLocalConversionRule.class); @@ -150,24 +156,39 @@ public class ThreadLocalConversionRule extends TypeConversionRule { public static TypeConversionDescriptor wrapWithNewExpression(PsiType to, PsiType from, PsiExpression initializer) { final String boxedTypeName = from instanceof PsiPrimitiveType ? ((PsiPrimitiveType)from).getBoxedTypeName() : from.getCanonicalText(); - return new TypeConversionDescriptor("$qualifier$", "new " + - to.getCanonicalText() + - "() {\n" + - "@Override \n" + - "protected " + - boxedTypeName + - " initialValue() {\n" + - " return " + - (PsiUtil.isLanguageLevel5OrHigher(initializer) - ? initializer.getText() - : (from instanceof PsiPrimitiveType ? "new " + - ((PsiPrimitiveType)from).getBoxedTypeName() + - "(" + - initializer.getText() + - ")" : initializer.getText())) + - ";\n" + - "}\n" + - "}", initializer); + List toMakeFinal = TypeConversionRuleUtil.getVariablesToMakeFinal(initializer); + if (toMakeFinal == null) return null; + return new WrappingWithInnerClassOrLambdaDescriptor("$qualifier$", + createThreadLocalInitializerReplacement(to, from, initializer, boxedTypeName), + initializer, + toMakeFinal); + } + + private static String createThreadLocalInitializerReplacement(PsiType to, + PsiType from, + PsiExpression initializer, + String boxedTypeName) { + if (PsiUtil.isLanguageLevel8OrHigher(initializer)) { + return "java.lang.ThreadLocal.withInitial(() -> " + initializer.getText() + ")"; + } + return "new " + + to.getCanonicalText() + + "() {\n" + + "@Override \n" + + "protected " + + boxedTypeName + + " initialValue() {\n" + + " return " + + (PsiUtil.isLanguageLevel5OrHigher(initializer) + ? initializer.getText() + : (from instanceof PsiPrimitiveType ? "new " + + ((PsiPrimitiveType)from).getBoxedTypeName() + + "(" + + initializer.getText() + + ")" : initializer.getText())) + + ";\n" + + "}\n" + + "}"; } private static String toPrimitive(String replaceByArg, PsiType from, PsiElement context) { @@ -221,5 +242,27 @@ public class ThreadLocalConversionRule extends TypeConversionRule { return toBoxed(arg, from, context); } + private static class WrappingWithInnerClassOrLambdaDescriptor extends TypeConversionDescriptor { + private final List myVariablesToMakeFinal; + private WrappingWithInnerClassOrLambdaDescriptor(@NonNls final String stringToReplace, + @NonNls final String replaceByString, + final PsiExpression expression, + @NotNull List toMakeFinal) { + super(stringToReplace, replaceByString, expression); + myVariablesToMakeFinal = toMakeFinal; + } + + @Override + public PsiExpression replace(PsiExpression expression, @NotNull TypeEvaluator evaluator) { + PsiExpression replaced = super.replace(expression, evaluator); + boolean atLeastJava8 = PsiUtil.isLanguageLevel8OrHigher(replaced); + for (PsiVariable var : myVariablesToMakeFinal) { + if (!atLeastJava8 || !HighlightControlFlowUtil.isEffectivelyFinal(var, replaced, null)) { + VariableAccessFromInnerClassFix.fixAccess(var, replaced); + } + } + return replaced; + } + } } \ No newline at end of file diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/TypeConversionRuleUtil.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/TypeConversionRuleUtil.java new file mode 100644 index 000000000000..6b4af6fdefa7 --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/TypeConversionRuleUtil.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2017 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.refactoring.typeMigration.rules; + +import com.intellij.psi.*; +import com.intellij.psi.controlFlow.*; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +class TypeConversionRuleUtil { + static List getVariablesToMakeFinal(@NotNull PsiExpression expression) { + final ControlFlow controlFlow; + try { + controlFlow = ControlFlowFactory.getInstance(expression.getProject()).getControlFlow(expression, new MyControlFlowPolicy(expression), false, false); + } + catch (AnalysisCanceledException e) { + return null; + } + + Collection writtenVariables = ControlFlowUtil.getWrittenVariables(controlFlow, 0, controlFlow.getSize(), false); + if (!writtenVariables.isEmpty()) return null; + + return ControlFlowUtil.getUsedVariables(controlFlow, 0, controlFlow.getSize()) + .stream() + .filter(v -> !v.hasModifierProperty(PsiModifier.FINAL)) + .collect(Collectors.toList()); + } + + private static class MyControlFlowPolicy implements ControlFlowPolicy { + private final PsiElement myElement; + + public MyControlFlowPolicy(PsiElement element) {myElement = element;} + + @Override + public PsiVariable getUsedVariable(@NotNull PsiReferenceExpression refExpr) { + if (refExpr.isQualified()) return null; + + PsiElement refElement = refExpr.resolve(); + if ((refElement instanceof PsiLocalVariable || refElement instanceof PsiParameter) && + !PsiTreeUtil.isAncestor(myElement, refElement, true)) { + return (PsiVariable) refElement; + } + + return null; + } + + @Override + public boolean isParameterAccepted(@NotNull PsiParameter psiParameter) { + return true; + } + + @Override + public boolean isLocalVariableAccepted(@NotNull PsiLocalVariable psiVariable) { + return true; + } + } +} diff --git a/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntention6Test.java b/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntention6Test.java new file mode 100644 index 000000000000..6b65b2c745dc --- /dev/null +++ b/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntention6Test.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2017 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.codeInsight; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.PlatformTestUtil; +import org.jetbrains.annotations.NotNull; + +public class ConvertToThreadLocalIntention6Test extends LightQuickFixParameterizedTestCase { + @Override + protected String getBasePath() { + return "/intentions/threadLocal6"; + } + + @NotNull + @Override + protected String getTestDataPath() { + return PlatformTestUtil.getCommunityPath() + "/java/typeMigration/testData"; + } + + public void test() throws Exception { + doAllTests(); + } + + @Override + protected LanguageLevel getLanguageLevel() { + return LanguageLevel.JDK_1_7; + } +} diff --git a/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntentionTest.java b/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntentionTest.java index e45b26ba77c8..6a358bbbb2d1 100644 --- a/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntentionTest.java +++ b/java/typeMigration/test/com/intellij/codeInsight/ConvertToThreadLocalIntentionTest.java @@ -8,11 +8,6 @@ import org.jetbrains.annotations.NotNull; * @author anna */ public class ConvertToThreadLocalIntentionTest extends LightQuickFixParameterizedTestCase { - @Override - protected boolean shouldBeAvailableAfterExecution() { - return true; - } - @Override protected String getBasePath() { return "/intentions/threadLocal"; diff --git a/java/typeMigration/test/com/intellij/refactoring/AllTypeMigrationTests.java b/java/typeMigration/test/com/intellij/refactoring/AllTypeMigrationTests.java index 32640fc85dde..331e5e279a2a 100644 --- a/java/typeMigration/test/com/intellij/refactoring/AllTypeMigrationTests.java +++ b/java/typeMigration/test/com/intellij/refactoring/AllTypeMigrationTests.java @@ -1,6 +1,7 @@ package com.intellij.refactoring; import com.intellij.codeInsight.ConvertToAtomicIntentionTest; +import com.intellij.codeInsight.ConvertToThreadLocalIntention6Test; import com.intellij.codeInsight.ConvertToThreadLocalIntentionTest; import com.intellij.codeInsight.inspections.GuavaInspectionTest; import junit.framework.Test; @@ -19,6 +20,7 @@ public class AllTypeMigrationTests { suite.addTestSuite(WildcardTypeMigrationTest.class); suite.addTestSuite(ConvertToAtomicIntentionTest.class); suite.addTestSuite(ConvertToThreadLocalIntentionTest.class); + suite.addTestSuite(ConvertToThreadLocalIntention6Test.class); suite.addTestSuite(GuavaInspectionTest.class); return suite; } diff --git a/java/typeMigration/test/com/intellij/refactoring/TypeMigrationByAtomicRuleTest.java b/java/typeMigration/test/com/intellij/refactoring/TypeMigrationByAtomicRuleTest.java index aeecfe6bfdc1..9e781175898f 100644 --- a/java/typeMigration/test/com/intellij/refactoring/TypeMigrationByAtomicRuleTest.java +++ b/java/typeMigration/test/com/intellij/refactoring/TypeMigrationByAtomicRuleTest.java @@ -100,4 +100,8 @@ public class TypeMigrationByAtomicRuleTest extends TypeMigrationTestBase{ public void testChainedInitialization() { doTestFieldType("a", myJavaFacade.getElementFactory().createTypeFromText("java.util.concurrent.atomic.AtomicInteger", null)); } + + public void testLiteralMigration() { + doTestFieldType("a", myJavaFacade.getElementFactory().createTypeFromText("java.util.concurrent.atomic.AtomicLong", null)); + } } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/atomic/afterAtomicArrayLength.java b/java/typeMigration/testData/intentions/atomic/afterAtomicArrayLength.java new file mode 100644 index 000000000000..76bd5940cb3a --- /dev/null +++ b/java/typeMigration/testData/intentions/atomic/afterAtomicArrayLength.java @@ -0,0 +1,10 @@ +import java.util.concurrent.atomic.AtomicIntegerArray; + +// "Convert to atomic" "true" +class Test { + final AtomicIntegerArray ii = new AtomicIntegerArray(new int[12]); + + void m() { + int k = ii.length(); + } +} diff --git a/java/typeMigration/testData/intentions/atomic/beforeAtomicArrayLength.java b/java/typeMigration/testData/intentions/atomic/beforeAtomicArrayLength.java new file mode 100644 index 000000000000..01fa0f5b0866 --- /dev/null +++ b/java/typeMigration/testData/intentions/atomic/beforeAtomicArrayLength.java @@ -0,0 +1,8 @@ +// "Convert to atomic" "true" +class Test { + int[] ii = new int[12]; + + void m() { + int k = ii.length; + } +} diff --git a/java/typeMigration/testData/intentions/threadLocal/after1.java b/java/typeMigration/testData/intentions/threadLocal/after1.java index b5df8fd35214..be391d4abec4 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after1.java +++ b/java/typeMigration/testData/intentions/threadLocal/after1.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return 0; - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> 0); void foo() { field.set(field.get() + 1); } diff --git a/java/typeMigration/testData/intentions/threadLocal/after2.java b/java/typeMigration/testData/intentions/threadLocal/after2.java index 3a546c5b0c9a..5f02c2f3925d 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after2.java +++ b/java/typeMigration/testData/intentions/threadLocal/after2.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected String initialValue() { - return ""; - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> ""); void foo() { System.out.println(field.get()); } diff --git a/java/typeMigration/testData/intentions/threadLocal/after3.java b/java/typeMigration/testData/intentions/threadLocal/after3.java index 9fc0f825ac4b..2e2f950d899a 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after3.java +++ b/java/typeMigration/testData/intentions/threadLocal/after3.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return new Integer(0); - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> new Integer(0)); void foo() { if (field.get() == null) return; } diff --git a/java/typeMigration/testData/intentions/threadLocal/after4.java b/java/typeMigration/testData/intentions/threadLocal/after4.java index 8410a1c33c0e..4dbc67bb1b47 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after4.java +++ b/java/typeMigration/testData/intentions/threadLocal/after4.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected String initialValue() { - return ""; - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> ""); void foo() { if (field.get().indexOf("a") == -1) return; } diff --git a/java/typeMigration/testData/intentions/threadLocal/after5.java b/java/typeMigration/testData/intentions/threadLocal/after5.java index 435e1b6a50e6..31ca64fbaaef 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after5.java +++ b/java/typeMigration/testData/intentions/threadLocal/after5.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return new Integer(0); - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> new Integer(0)); void foo(Test t) { if (t.field.get() == null) return; } diff --git a/java/typeMigration/testData/intentions/threadLocal/after6.java b/java/typeMigration/testData/intentions/threadLocal/after6.java index 1879a46be16d..d153a6579061 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after6.java +++ b/java/typeMigration/testData/intentions/threadLocal/after6.java @@ -2,11 +2,6 @@ class Test { static final ThreadLocal field; static { - field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return new Integer(0); - } - }; + field = ThreadLocal.withInitial(() -> new Integer(0)); } } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/after7.java b/java/typeMigration/testData/intentions/threadLocal/after7.java index 5398ff5e2991..14fa782d7060 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after7.java +++ b/java/typeMigration/testData/intentions/threadLocal/after7.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class X { - private final ThreadLocal bytes = new ThreadLocal() { - @Override - protected byte[] initialValue() { - return new byte[10]; - } - }; + private final ThreadLocal bytes = ThreadLocal.withInitial(() -> new byte[10]); byte foo(byte b) { bytes.get()[0] = 1; diff --git a/java/typeMigration/testData/intentions/threadLocal/after8.java b/java/typeMigration/testData/intentions/threadLocal/after8.java index d158894b6130..2d4aa4b61658 100644 --- a/java/typeMigration/testData/intentions/threadLocal/after8.java +++ b/java/typeMigration/testData/intentions/threadLocal/after8.java @@ -1,9 +1,4 @@ // "Convert to ThreadLocal" "true" class X { - final ThreadLocal i = new ThreadLocal() { - @Override - protected Integer initialValue() { - return 0; - } - }; + final ThreadLocal i = ThreadLocal.withInitial(() -> 0); } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterArrayInitializer.java b/java/typeMigration/testData/intentions/threadLocal/afterArrayInitializer.java index 59323f18e0e5..dde597cbc1da 100644 --- a/java/typeMigration/testData/intentions/threadLocal/afterArrayInitializer.java +++ b/java/typeMigration/testData/intentions/threadLocal/afterArrayInitializer.java @@ -1,9 +1,4 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected String[] initialValue() { - return new String[]{}; - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> new String[]{}); } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java b/java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java new file mode 100644 index 000000000000..654691a7f2c9 --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal/afterFieldAssignmentFromNonFinalParameter.java @@ -0,0 +1,12 @@ +// "Convert to ThreadLocal" "true" +class Main { + private final ThreadLocal property; + + Main3(boolean property) { + if (property) { + property = false; + } + boolean finalProperty = property; + this.property = ThreadLocal.withInitial(() -> finalProperty); + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterFinalField.java b/java/typeMigration/testData/intentions/threadLocal/afterFinalField.java new file mode 100644 index 000000000000..da8f5235f329 --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal/afterFinalField.java @@ -0,0 +1,8 @@ +// "Convert to ThreadLocal" "true" +class Foo { + private final ThreadLocal property; + + Foo(boolean property) { + this.property = ThreadLocal.withInitial(() -> property); + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterNormalize.java b/java/typeMigration/testData/intentions/threadLocal/afterNormalize.java index 1b2532d9d1de..9c7a8342ebe8 100644 --- a/java/typeMigration/testData/intentions/threadLocal/afterNormalize.java +++ b/java/typeMigration/testData/intentions/threadLocal/afterNormalize.java @@ -1,11 +1,6 @@ // "Convert to ThreadLocal" "true" class X { - private final ThreadLocal s = new ThreadLocal() { - @Override - protected String initialValue() { - return ""; - } - }; + private final ThreadLocal s = ThreadLocal.withInitial(() -> ""); private String t; private String u; } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterPrimitiveNoInitializer.java b/java/typeMigration/testData/intentions/threadLocal/afterPrimitiveNoInitializer.java index b922e385cde7..2560a2a708d6 100644 --- a/java/typeMigration/testData/intentions/threadLocal/afterPrimitiveNoInitializer.java +++ b/java/typeMigration/testData/intentions/threadLocal/afterPrimitiveNoInitializer.java @@ -1,9 +1,4 @@ // "Convert to ThreadLocal" "true" class Test { - final ThreadLocal field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return 0; - } - }; + final ThreadLocal field = ThreadLocal.withInitial(() -> 0); } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/afterTA1.java b/java/typeMigration/testData/intentions/threadLocal/afterTA1.java index 0e3687e146b6..d1da14e75739 100644 --- a/java/typeMigration/testData/intentions/threadLocal/afterTA1.java +++ b/java/typeMigration/testData/intentions/threadLocal/afterTA1.java @@ -5,10 +5,5 @@ import java.lang.annotation.*; public @interface TA { int value(); } class Test { - final ThreadLocal<@TA(42) Integer> field = new ThreadLocal() { - @Override - protected Integer initialValue() { - return 0; - } - }; + final ThreadLocal<@TA(42) Integer> field = ThreadLocal.withInitial(() -> 0); } \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java b/java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java new file mode 100644 index 000000000000..48a644f90053 --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal/beforeFieldAssignmentFromNonFinalParameter.java @@ -0,0 +1,11 @@ +// "Convert to ThreadLocal" "true" +class Main { + private final boolean property; + + Main3(boolean property) { + if (property) { + property = false; + } + this.property = property; + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal/beforeFinalField.java b/java/typeMigration/testData/intentions/threadLocal/beforeFinalField.java new file mode 100644 index 000000000000..1cffae690828 --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal/beforeFinalField.java @@ -0,0 +1,8 @@ +// "Convert to ThreadLocal" "true" +class Foo { + private final boolean property; + + Foo(boolean property) { + this.property = property; + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal6/afterJava6.java b/java/typeMigration/testData/intentions/threadLocal6/afterJava6.java new file mode 100644 index 000000000000..77bd711defff --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal6/afterJava6.java @@ -0,0 +1,13 @@ +// "Convert to ThreadLocal" "true" +class Main { + private final ThreadLocal property; + + Main3(final boolean property) { + this.property = new ThreadLocal() { + @Override + protected Boolean initialValue() { + return property; + } + }; + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/threadLocal6/beforeJava6.java b/java/typeMigration/testData/intentions/threadLocal6/beforeJava6.java new file mode 100644 index 000000000000..acc57b15ecde --- /dev/null +++ b/java/typeMigration/testData/intentions/threadLocal6/beforeJava6.java @@ -0,0 +1,8 @@ +// "Convert to ThreadLocal" "true" +class Main { + private final boolean property; + + Main3(boolean property) { + this.property = property; + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.items b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.items new file mode 100644 index 000000000000..33f6b9a33cf9 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.items @@ -0,0 +1,8 @@ +Types: +PsiField:a : java.util.concurrent.atomic.AtomicLong + +Conversions: +100 -> new java.util.concurrent.atomic.AtomicLong($val$) $val$ 100 + +New expression type changes: +Fails: diff --git a/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.java b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.java new file mode 100644 index 000000000000..ec7370c3df80 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/after/Test.java @@ -0,0 +1,5 @@ +import java.util.concurrent.atomic.AtomicLong; + +class Test { + AtomicLong a = new AtomicLong(100); +} \ No newline at end of file diff --git a/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/before/Test.java b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/before/Test.java new file mode 100644 index 000000000000..e229caf913c4 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigrationByAtomic/literalMigration/before/Test.java @@ -0,0 +1,3 @@ +class Test { + long a = 100; +} \ No newline at end of file diff --git a/native/WinElevator/elevator/elevator.c b/native/WinElevator/elevator/elevator.c index bad1ed9b0f2c..0fd99fd6d8e7 100644 --- a/native/WinElevator/elevator/elevator.c +++ b/native/WinElevator/elevator/elevator.c @@ -53,13 +53,7 @@ static DWORD _ConnectIfNeededPipe(DWORD nParentPid, DWORD nDescriptor, FILE* str if (!SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, TRUE)) { return GetLastError(); - } - - // Fix CRT - if (_dup2(_open_osfhandle((intptr_t)hPipe, _O_WTEXT | _O_TEXT), _fileno(stream)) != 0) - { - return errno; - } + } // Fix Win32API DWORD hStdHandleToChange = ELEV_DESCR_GET_HANDLE(nDescriptor); diff --git a/platform/build-scripts/tools/mac/scripts/makedmg.sh b/platform/build-scripts/tools/mac/scripts/makedmg.sh index ae3b2aad4b14..c525b8bf6f52 100644 --- a/platform/build-scripts/tools/mac/scripts/makedmg.sh +++ b/platform/build-scripts/tools/mac/scripts/makedmg.sh @@ -36,6 +36,26 @@ stat ${EXPLODED}/DSStorePlaceHolder echo "Creating unpacked r/w disk image ${VOLNAME}..." hdiutil create -srcfolder ./${EXPLODED} -volname "$VOLNAME" -anyowners -nospotlight -quiet -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW $2.temp.dmg +# check if the image already mounted +if [ -d "/Volumes/$VOLNAME" ]; then + attempt=1 + limit=5 + while [ $attempt -le $limit ] + do + echo "/Volumes/$VOLNAME - the image is already mounted. This build will wait for unmount for 1 min (up to 5 times)." + sleep 60; + if [ -d "/Volumes/$VOLNAME" ]; then + let "attempt += 1" + if [ $attempt -eq $limit ]; then + echo "/Volumes/$VOLNAME - the image is still mounted. By the reason the build will be stopped." + rm -rf ${EXPLODED} + rm -f $2.temp.dmg + exit 1 + fi + fi + done +fi + # mount this image echo "Mounting unpacked r/w disk image..." device=$(hdiutil attach -readwrite -noverify -noautoopen $2.temp.dmg | egrep '^/dev/' | sed 1q | awk '{print $1.dmg}') diff --git a/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java b/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java index ed907a2b696d..78ff118edeeb 100644 --- a/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java +++ b/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java @@ -30,7 +30,9 @@ import com.intellij.psi.search.PsiElementProcessor.FindElement; import com.intellij.psi.stubs.StubBase; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.templateLanguages.OuterLanguageElement; +import com.intellij.psi.tree.IElementType; import com.intellij.util.ArrayUtil; +import com.intellij.util.Consumer; import com.intellij.util.PairProcessor; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; @@ -661,6 +663,32 @@ public class PsiTreeUtil { return (T)element; } + @Nullable + public static PsiElement findSiblingForward(@NotNull final PsiElement element, + @NotNull final IElementType elementType, + @Nullable final Consumer consumer) { + for (PsiElement e = element.getNextSibling(); e != null; e = e.getNextSibling()) { + if (elementType.equals(e.getNode().getElementType())) { + return e; + } + if (consumer != null) consumer.consume(e); + } + return null; + } + + @Nullable + public static PsiElement findSiblingBackward(@NotNull final PsiElement element, + @NotNull final IElementType elementType, + @Nullable final Consumer consumer) { + for (PsiElement e = element.getPrevSibling(); e != null; e = e.getPrevSibling()) { + if (elementType.equals(e.getNode().getElementType())) { + return e; + } + if (consumer != null) consumer.consume(e); + } + return null; + } + @Nullable @Contract("null, _ -> null") public static PsiElement skipSiblingsForward(@Nullable PsiElement element, @NotNull Class... elementClasses) { diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java index 1f3a87c7917f..6253c4b9be90 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java @@ -34,7 +34,6 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import javax.swing.event.HyperlinkEvent; import java.util.List; @@ -65,7 +64,6 @@ public abstract class DvcsBranchPopup { ? " in " + DvcsUtil.getShortRepositoryName(currentRepository) : ""; myPopup = new BranchActionGroupPopup(title + myRepoTitleInfo, myProject, preselectActionCondition, createActions(), dimensionKey); initBranchSyncPolicyIfNotInitialized(); - setCurrentBranchInfo(); warnThatBranchesDivergedIfNeeded(); } @@ -86,11 +84,6 @@ public abstract class DvcsBranchPopup { } } - protected void setCurrentBranchInfo() { - String branchText = "Current branch : "; - myPopup.setAdText(branchText + myCurrentRepository.getCurrentBranchName(), SwingConstants.CENTER); - } - private void notifyAboutSyncedBranches() { String description = "You have several " + myVcs.getDisplayName() + " roots in the project and they all are checked out at the same branch. " + @@ -144,11 +137,15 @@ public abstract class DvcsBranchPopup { } private void warnThatBranchesDivergedIfNeeded() { - if (myRepositoryManager.moreThanOneRoot() && myMultiRootBranchConfig.diverged() && userWantsSyncControl()) { + if (isBranchesDiverged()) { myPopup.setWarning("Branches have diverged"); } } + protected boolean isBranchesDiverged() { + return myRepositoryManager.moreThanOneRoot() && myMultiRootBranchConfig.diverged() && userWantsSyncControl(); + } + @NotNull protected abstract DefaultActionGroup createRepositoriesActions(); diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java index ee24209167fa..d999bf5334c8 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.function.Function; public abstract class DvcsMultiRootBranchConfig { @NotNull protected final Collection myRepositories; @@ -34,9 +35,14 @@ public abstract class DvcsMultiRootBranchConfig { @Nullable public String getCurrentBranch() { + return getCommonName(Repository::getCurrentBranchName); + } + + @Nullable + public String getCommonName(@NotNull Function nameSupplier) { String commonBranch = null; for (Repo repository : myRepositories) { - String branchName = repository.getCurrentBranchName(); + String branchName = nameSupplier.apply(repository); if (branchName == null) { return null; } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java index 17445b9d0402..fdc42fc91cd3 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java @@ -15,6 +15,7 @@ */ package com.intellij.dvcs.ui; +import com.intellij.icons.AllIcons; import com.intellij.ide.DataManager; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.*; @@ -28,10 +29,7 @@ import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.WindowStateService; import com.intellij.openapi.vcs.ui.FlatSpeedSearchPopup; -import com.intellij.ui.ErrorLabel; -import com.intellij.ui.JBColor; -import com.intellij.ui.ScrollingUtil; -import com.intellij.ui.SeparatorWithText; +import com.intellij.ui.*; import com.intellij.ui.components.panels.OpaquePanel; import com.intellij.ui.popup.KeepingPopupOpenAction; import com.intellij.ui.popup.PopupFactoryImpl; @@ -40,6 +38,7 @@ import com.intellij.ui.popup.list.IconListPopupRenderer; import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.ui.popup.list.ListPopupModel; import com.intellij.ui.popup.list.PopupListElementRenderer; +import com.intellij.util.FontUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.JBUI; @@ -60,11 +59,14 @@ import static com.intellij.util.ui.UIUtil.DEFAULT_VGAP; public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private static final DataKey POPUP_MODEL = DataKey.create("VcsPopupModel"); + private Project myProject; private MyPopupListElementRenderer myListElementRenderer; private boolean myShown; - @NotNull private Dimension myPrevSize = JBUI.emptySize(); private boolean myUserSizeChanged; - private Project myProject; + private boolean myInternalSizeChanged; + private int myMeanRowHeight; + @Nullable private final String myKey; + @NotNull private Dimension myPrevSize = JBUI.emptySize(); public BranchActionGroupPopup(@NotNull String title, @NotNull Project project, @@ -76,27 +78,62 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { myProject = project; DataManager.registerDataProvider(getList(), dataId -> POPUP_MODEL.is(dataId) ? getListModel() : null); installOnHoverIconsSupport(getListElementRenderer()); - if (dimensionKey != null) { - Dimension storedSize = WindowStateService.getInstance(myProject).getSizeFor(myProject, dimensionKey); + myKey = dimensionKey; + if (myKey != null) { + Dimension storedSize = WindowStateService.getInstance(myProject).getSizeFor(myProject, myKey); if (storedSize != null) { //set forced size before component is shown setSize(storedSize); } + createTitlePanelToolbar(myKey); } - trackDimensions(dimensionKey); + myMeanRowHeight = getList().getCellBounds(0, 0).height + UIUtil.getListCellVPadding() * 2; + } + + void createTitlePanelToolbar(@NotNull String dimensionKey) { + AnAction restoreDefaultSizeAction = + new DumbAwareAction("Restore Size", "Restore default size for widget", AllIcons.Vcs.RestoreDefaultSize) { + @Override + public void actionPerformed(AnActionEvent e) { + WindowStateService.getInstance(myProject).putSizeFor(myProject, dimensionKey, null); + myInternalSizeChanged = true; + pack(true, true); + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabledAndVisible(myUserSizeChanged || + WindowStateService.getInstance(myProject).getSizeFor(myProject, dimensionKey) != null); + } + }; + final ActionToolbar popupTitleToolbar = ActionManager.getInstance() + .createActionToolbar("BranchWidget", new DefaultActionGroup(restoreDefaultSizeAction), true); + final JComponent toolbarComponent = popupTitleToolbar.getComponent(); + popupTitleToolbar.setReservePlaceAutoPopupIcon(false); + toolbarComponent.setBorder(JBUI.Borders.emptyRight(2)); + toolbarComponent.setOpaque(false); + + getTitle().setButtonComponent(new ActiveComponent.Adapter() { + @Override + public JComponent getComponent() { + return toolbarComponent; + } + }, null); } //for child popups only private BranchActionGroupPopup(@Nullable WizardPopup aParent, @NotNull ListPopupStep aStep, @Nullable Object parentValue) { super(aParent, aStep, DataContext.EMPTY_CONTEXT, parentValue); + // don't store children popup userSize; + myKey = null; DataManager.registerDataProvider(getList(), dataId -> POPUP_MODEL.is(dataId) ? getListModel() : null); installOnHoverIconsSupport(getListElementRenderer()); - // don't store children popup userSize; - trackDimensions(null); } private void trackDimensions(@Nullable String dimensionKey) { - getComponent().addComponentListener(new ComponentAdapter() { + Window popupWindow = getPopupWindow(); + if (popupWindow == null) return; + popupWindow.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { if (myShown) { @@ -118,7 +155,11 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private void processOnSizeChanged() { Dimension newSize = ObjectUtils.assertNotNull(getSize()); - if (myPrevSize.height < newSize.height) { + int preferredHeight = getComponent().getPreferredSize().height; + int realHeight = getComponent().getHeight(); + boolean shouldExpand = preferredHeight + myMeanRowHeight < realHeight; + boolean sizeWasIncreased = myPrevSize.height < newSize.height; + if (!myInternalSizeChanged && sizeWasIncreased && shouldExpand) { List mores = getMoreActions(); for (MoreAction more : mores) { if (!getList().getScrollableTracksViewportHeight()) break; @@ -129,7 +170,9 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { } } myPrevSize = newSize; - myUserSizeChanged = true; + //ugly properties to distinguish user size changed from pack method call after Restore Size action performed + myUserSizeChanged = !myInternalSizeChanged; + myInternalSizeChanged = false; } @NotNull @@ -165,6 +208,7 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { if (size != null) { myPrevSize = size; } + trackDimensions(myKey); } private static void createSpeedSearchActions(@NotNull ActionGroup actionGroup, @@ -246,12 +290,7 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { @Override protected WizardPopup createPopup(WizardPopup parent, PopupStep step, Object parentValue) { - WizardPopup popup = createListPopupStep(parent, step, parentValue); - RootAction rootAction = getRootAction(parentValue); - if (rootAction != null) { - popup.setAdText((rootAction).getCaption()); - } - return popup; + return createListPopupStep(parent, step, parentValue); } private WizardPopup createListPopupStep(WizardPopup parent, PopupStep step, Object parentValue) { @@ -261,11 +300,6 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { return super.createPopup(parent, step, parentValue); } - @Nullable - private static RootAction getRootAction(Object value) { - return getSpecificAction(value, RootAction.class); - } - private static T getSpecificAction(Object value, @NotNull Class clazz) { if (value instanceof PopupFactoryImpl.ActionItem) { AnAction action = ((PopupFactoryImpl.ActionItem)value).getAction(); @@ -290,6 +324,7 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private class MyPopupListElementRenderer extends PopupListElementRenderer implements IconListPopupRenderer { + private ErrorLabel myPrefixLabel; private ErrorLabel myInfoLabel; private IconComponent myIconLabel; @@ -328,26 +363,36 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { } myIconLabel.setIcon(myDescriptor.getIconFor(value)); PopupElementWithAdditionalInfo additionalInfoAction = getSpecificAction(value, PopupElementWithAdditionalInfo.class); - String infoText = additionalInfoAction != null ? additionalInfoAction.getInfoText() : null; + updateInfoComponent(myPrefixLabel, additionalInfoAction != null ? additionalInfoAction.getPrefixInfo() : null, isSelected); + updateInfoComponent(myInfoLabel, additionalInfoAction != null ? additionalInfoAction.getInfoText() : null, isSelected); + } + + private void updateInfoComponent(@NotNull ErrorLabel infoLabel, @Nullable String infoText, boolean isSelected) { if (infoText != null) { - myInfoLabel.setVisible(true); - myInfoLabel.setText(infoText); + infoLabel.setVisible(true); + infoLabel.setText(infoText); if (isSelected) { - setSelected(myInfoLabel); + setSelected(infoLabel); } else { - myInfoLabel.setBackground(getBackground()); - myInfoLabel.setForeground(JBColor.GRAY); // different foreground than for other elements + infoLabel.setBackground(getBackground()); + infoLabel.setForeground(JBColor.GRAY); // different foreground than for other elements } } else { - myInfoLabel.setVisible(false); + infoLabel.setVisible(false); } } @Override protected JComponent createItemComponent() { + myPrefixLabel = new ErrorLabel(); + myPrefixLabel.setOpaque(true); + myPrefixLabel.setBorder(JBUI.Borders.empty(1, 1, 1, DEFAULT_HGAP)); + Font minusOneFont = FontUtil.minusOne(myPrefixLabel.getFont()); + myPrefixLabel.setFont(minusOneFont); + myTextLabel = new ErrorLabel(); myTextLabel.setOpaque(true); myTextLabel.setBorder(JBUI.Borders.empty(1)); @@ -355,15 +400,19 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { myInfoLabel = new ErrorLabel(); myInfoLabel.setOpaque(true); myInfoLabel.setBorder(JBUI.Borders.empty(1, DEFAULT_HGAP, 1, 1)); + myInfoLabel.setFont(minusOneFont); JPanel compoundPanel = new OpaquePanel(new BorderLayout(), JBColor.WHITE); myIconLabel = new IconComponent(); myInfoLabel.setHorizontalAlignment(SwingConstants.RIGHT); + JPanel compoundTextPanel = new OpaquePanel(new BorderLayout(), compoundPanel.getBackground()); JPanel textPanel = new OpaquePanel(new BorderLayout(), compoundPanel.getBackground()); compoundPanel.add(myIconLabel, BorderLayout.WEST); textPanel.add(myTextLabel, BorderLayout.WEST); textPanel.add(myInfoLabel, BorderLayout.CENTER); - compoundPanel.add(textPanel, BorderLayout.CENTER); + compoundTextPanel.add(myPrefixLabel, BorderLayout.WEST); + compoundTextPanel.add(textPanel, BorderLayout.CENTER); + compoundPanel.add(compoundTextPanel, BorderLayout.CENTER); return layoutComponent(compoundPanel); } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/PopupElementWithAdditionalInfo.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/PopupElementWithAdditionalInfo.java index d59929095a9f..b7508b845dfb 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/PopupElementWithAdditionalInfo.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/PopupElementWithAdditionalInfo.java @@ -19,5 +19,8 @@ import org.jetbrains.annotations.Nullable; public interface PopupElementWithAdditionalInfo { @Nullable - String getInfoText(); + default String getInfoText() {return null;} + + @Nullable + default String getPrefixInfo() {return null;} } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/RootAction.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/RootAction.java index 7e9f5a22778f..e407d1a5704e 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/RootAction.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/RootAction.java @@ -31,9 +31,9 @@ public class RootAction extends ActionGroup implements Pop @NotNull protected final T myRepository; @NotNull private final ActionGroup myGroup; - @NotNull private final String myBranchText; + @Nullable private final String myBranchText; - public RootAction(@NotNull T repository, @NotNull ActionGroup actionsGroup, @NotNull String branchText) { + public RootAction(@NotNull T repository, @NotNull ActionGroup actionsGroup, @Nullable String branchText) { super("", true); myRepository = repository; myGroup = actionsGroup; @@ -41,18 +41,13 @@ public class RootAction extends ActionGroup implements Pop getTemplatePresentation().setText(DvcsUtil.getShortRepositoryName(repository), false); } - @NotNull - public String getCaption() { - return "Current branch in " + DvcsUtil.getShortRepositoryName(myRepository) + ": " + getInfoText(); - } - @NotNull @Override public AnAction[] getChildren(@Nullable AnActionEvent e) { return myGroup.getChildren(e); } - @NotNull + @Nullable @Override public String getInfoText() { return myBranchText; diff --git a/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsUtil.java b/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsUtil.java new file mode 100644 index 000000000000..3393e3f517e8 --- /dev/null +++ b/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsUtil.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2017 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.ui.breadcrumbs; + +import com.intellij.lang.Language; +import org.jetbrains.annotations.NotNull; + +public class BreadcrumbsUtil { + + public static BreadcrumbsProvider getInfoProvider(@NotNull Language language) { + BreadcrumbsProvider[] providers = BreadcrumbsProvider.EP_NAME.getExtensions(); + while (language != null) { + for (BreadcrumbsProvider provider : providers) { + for (Language supported : provider.getLanguages()) { + if (language.is(supported)) { + return provider; + } + } + } + language = language.getBaseLanguage(); + } + return null; + } +} diff --git a/platform/icons/src/vcs/restoreDefaultSize.png b/platform/icons/src/vcs/restoreDefaultSize.png new file mode 100644 index 000000000000..c3dd16b7aa69 Binary files /dev/null and b/platform/icons/src/vcs/restoreDefaultSize.png differ diff --git a/platform/icons/src/vcs/restoreDefaultSize@2x.png b/platform/icons/src/vcs/restoreDefaultSize@2x.png new file mode 100644 index 000000000000..7b9dbc291778 Binary files /dev/null and b/platform/icons/src/vcs/restoreDefaultSize@2x.png differ diff --git a/platform/icons/src/vcs/restoreDefaultSize@2x_dark.png b/platform/icons/src/vcs/restoreDefaultSize@2x_dark.png new file mode 100644 index 000000000000..6a357c9e36b6 Binary files /dev/null and b/platform/icons/src/vcs/restoreDefaultSize@2x_dark.png differ diff --git a/platform/icons/src/vcs/restoreDefaultSize_dark.png b/platform/icons/src/vcs/restoreDefaultSize_dark.png new file mode 100644 index 000000000000..ed9d8114615c Binary files /dev/null and b/platform/icons/src/vcs/restoreDefaultSize_dark.png differ diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java b/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java index 893da0cf4387..3c2d23f53478 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/AbstractFontOptionsPanel.java @@ -23,7 +23,6 @@ import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.FontPreferences; import com.intellij.openapi.editor.colors.ModifiableFontPreferences; -import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.FontComboBox; @@ -32,7 +31,6 @@ import com.intellij.ui.TooltipWithClickableLinks; import com.intellij.ui.components.JBCheckBox; import com.intellij.util.EventDispatcher; import com.intellij.util.ui.JBUI; -import net.miginfocom.swing.MigLayout; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -61,9 +59,8 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options @NotNull private final JTextField myEditorFontSizeField = new JTextField(4); @NotNull private final JTextField myLineSpacingField = new JTextField(4); private final FontComboBox myPrimaryCombo = new FontComboBox(); - private final JCheckBox myUseSecondaryFontCheckbox = new JCheckBox(ApplicationBundle.message("secondary.font")); private final JCheckBox myEnableLigaturesCheckbox = new JCheckBox(ApplicationBundle.message("use.ligatures")); - private final FontComboBox mySecondaryCombo = new FontComboBox(false, false); + private final FontComboBox mySecondaryCombo = new FontComboBox(false, false, true); @NotNull private final JBCheckBox myOnlyMonospacedCheckBox = new JBCheckBox(ApplicationBundle.message("checkbox.show.only.monospaced.fonts")); @@ -72,30 +69,72 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options private JLabel myPrimaryLabel; private JLabel mySizeLabel; + protected final static int ADDITIONAL_VERTICAL_GAP = 12; + protected final static int BASE_INSET = 5; + private JLabel mySecondaryFontLabel; + private JLabel myLineSpacingLabel; protected AbstractFontOptionsPanel() { - setLayout(new MigLayout("ins 0, gap 5, flowx")); - initControls(); + setLayout(new FlowLayout(FlowLayout.LEFT)); + add(createControls()); + } + + protected JComponent createControls() { + return createFontSettingsPanel(); } @SuppressWarnings("unchecked") - protected void initControls() { - add(myOnlyMonospacedCheckBox, "newline 10, sgx b, sx 2"); + protected final JPanel createFontSettingsPanel() { + Insets baseInsets = getInsets(0, 0); + JPanel fontPanel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.WEST; + c.insets = baseInsets; + + c.gridx = 0; + c.gridy = 0; myPrimaryLabel = new JLabel(ApplicationBundle.message("primary.font")); - add(myPrimaryLabel, "newline, ax right"); - add(myPrimaryCombo, "sgx b"); + fontPanel.add(myPrimaryLabel, c); + + c.gridx = 1; + fontPanel.add(myPrimaryCombo, c); + + c.gridx = 2; + c.insets = getInsets(0, BASE_INSET); + fontPanel.add(myOnlyMonospacedCheckBox, c); + + c.gridx = 0; + c.gridy = 1; + c.insets = baseInsets; mySizeLabel = new JLabel(ApplicationBundle.message("editbox.font.size")); - add(mySizeLabel, "gapleft 20"); - add(myEditorFontSizeField); - add(new JLabel(ApplicationBundle.message("editbox.line.spacing")), "gapleft 20"); - add(myLineSpacingField); + fontPanel.add(mySizeLabel, c); + + c.gridx = 1; + fontPanel.add(myEditorFontSizeField, c); + + c.gridx = 0; + c.gridy = 2; + myLineSpacingLabel = new JLabel(ApplicationBundle.message("editbox.line.spacing")); + myLineSpacingLabel.setLabelFor(myLineSpacingField); + fontPanel.add(myLineSpacingLabel, c); + c.gridx = 1; + fontPanel.add(myLineSpacingField,c); + + c.gridy = 3; + c.gridx = 0; + c.insets = getInsets(ADDITIONAL_VERTICAL_GAP, 0); + mySecondaryFontLabel = new JLabel(ApplicationBundle.message("secondary.font")); + mySecondaryFontLabel.setLabelFor(mySecondaryCombo); + fontPanel.add(mySecondaryFontLabel, c); + c.gridx = 1; + fontPanel.add(mySecondaryCombo, c); + c.gridx = 2; + c.insets = getInsets(ADDITIONAL_VERTICAL_GAP, BASE_INSET); + JLabel fallbackLabel = new JLabel(ApplicationBundle.message("label.fallback.fonts.list.description")); + fallbackLabel.setEnabled(false); + fontPanel.add(fallbackLabel, c); - add(new JLabel(ApplicationBundle.message("label.fallback.fonts.list.description"), - MessageType.INFO.getDefaultIcon(), - SwingConstants.LEFT), "newline, sx 5"); - add(myUseSecondaryFontCheckbox, "newline, ax right"); - add(mySecondaryCombo, "sgx b"); JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); myEnableLigaturesCheckbox.setBorder(null); panel.add(myEnableLigaturesCheckbox); @@ -108,10 +147,14 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options warningIcon.setBorder(JBUI.Borders.emptyLeft(5)); warningIcon.setVisible(!SystemInfo.isJetBrainsJvm); panel.add(warningIcon); - add(panel, "newline, sx 2"); + c.gridx = 0; + c.gridy = 4; + c.gridwidth = 2; + c.insets = getInsets(ADDITIONAL_VERTICAL_GAP, 0); + c.insets.bottom = BASE_INSET; + fontPanel.add(panel, c); myOnlyMonospacedCheckBox.setBorder(null); - myUseSecondaryFontCheckbox.setBorder(null); mySecondaryCombo.setEnabled(false); myOnlyMonospacedCheckBox.setSelected(EditorColorsManager.getInstance().isUseOnlyMonospacedFonts()); @@ -126,10 +169,6 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options mySecondaryCombo.setMonospacedOnly(myOnlyMonospacedCheckBox.isSelected()); mySecondaryCombo.setRenderer(RENDERER); - myUseSecondaryFontCheckbox.addActionListener(e -> { - mySecondaryCombo.setEnabled(myUseSecondaryFontCheckbox.isSelected()); - syncFontFamilies(); - }); ItemListener itemListener = this::syncFontFamilies; myPrimaryCombo.addItemListener(itemListener); mySecondaryCombo.addItemListener(itemListener); @@ -198,6 +237,11 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options updateDescription(true); } }); + return fontPanel; + } + + private static Insets getInsets(int extraTopSpacing, int extraLeftSpacing) { + return JBUI.insets(BASE_INSET + extraTopSpacing, BASE_INSET + extraLeftSpacing, 0, 0); } protected void setDelegatingPreferences(boolean isDelegating) { @@ -249,7 +293,7 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options modifiableFontPreferences.clearFonts(); modifiableFontPreferences.setUseLigatures(myEnableLigaturesCheckbox.isSelected()); String primaryFontFamily = myPrimaryCombo.getFontName(); - String secondaryFontFamily = mySecondaryCombo.isEnabled() ? mySecondaryCombo.getFontName() : null; + String secondaryFontFamily = mySecondaryCombo.isNoFontSelected() ? null : mySecondaryCombo.getFontName(); int fontSize = getFontSizeFromField(); if (primaryFontFamily != null) { if (!FontPreferences.DEFAULT_FONT_NAME.equals(primaryFontFamily)) { @@ -276,7 +320,6 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options List fontFamilies = fontPreferences.getEffectiveFontFamilies(); myPrimaryCombo.setFontName(fontPreferences.getFontFamily()); boolean isThereSecondaryFont = fontFamilies.size() > 1; - myUseSecondaryFontCheckbox.setSelected(isThereSecondaryFont); mySecondaryCombo.setFontName(isThereSecondaryFont ? fontFamilies.get(1) : null); myEditorFontSizeField.setText(String.valueOf(fontPreferences.getSize(fontPreferences.getFontFamily()))); @@ -285,12 +328,13 @@ public abstract class AbstractFontOptionsPanel extends JPanel implements Options boolean readOnly = isReadOnlyColorScheme || !(getFontPreferences() instanceof ModifiableFontPreferences); myPrimaryCombo.setEnabled(!readOnly); myPrimaryLabel.setEnabled(!readOnly); - mySecondaryCombo.setEnabled(isThereSecondaryFont && !readOnly); + mySecondaryCombo.setEnabled(!readOnly); + mySecondaryFontLabel.setEnabled(!readOnly); myOnlyMonospacedCheckBox.setEnabled(!readOnly); myLineSpacingField.setEnabled(!readOnly); + myLineSpacingLabel.setEnabled(!readOnly); myEditorFontSizeField.setEnabled(!readOnly); mySizeLabel.setEnabled(!readOnly); - myUseSecondaryFontCheckbox.setEnabled(!readOnly); myEnableLigaturesCheckbox.setEnabled(!readOnly && SystemInfo.isJetBrainsJvm); myEnableLigaturesCheckbox.setSelected(fontPreferences.useLigatures()); diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/ConsoleFontOptions.java b/platform/lang-impl/src/com/intellij/application/options/colors/ConsoleFontOptions.java index 6a1f8fcd3c51..35c0886248c6 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/ConsoleFontOptions.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/ConsoleFontOptions.java @@ -31,8 +31,13 @@ public class ConsoleFontOptions extends FontOptions { @Nullable @Override - protected String getInheritFontTitle() { - return "editor font"; + protected String getInheritedFontTitle() { + return "Scheme font"; + } + + @Override + protected String getOverwriteFontTitle() { + return "Set console font for color scheme"; } @Override @@ -55,6 +60,11 @@ public class ConsoleFontOptions extends FontOptions { return getCurrentScheme().getConsoleFontPreferences(); } + @Override + protected FontPreferences getBaseFontPreferences() { + return getCurrentScheme().getFontPreferences(); + } + @Override protected void setDelegatingPreferences(boolean isDelegating) { FontPreferences currPrefs = getCurrentScheme().getConsoleFontPreferences(); diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java b/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java index 0f790fad6150..fb25dafa7024 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/FontOptions.java @@ -21,9 +21,13 @@ import com.intellij.ide.DataManager; import com.intellij.openapi.editor.colors.DelegatingFontPreferences; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.FontPreferences; +import com.intellij.openapi.editor.colors.impl.AppEditorFontOptions; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ex.Settings; import com.intellij.ui.HoverHyperlinkLabel; +import com.intellij.ui.JBColor; +import com.intellij.util.ui.JBDimension; +import com.intellij.util.ui.JBUI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,24 +43,53 @@ public class FontOptions extends AbstractFontOptionsPanel { @NotNull private final ColorAndFontOptions myOptions; private @Nullable JCheckBox myInheritFontCheckbox; + private @Nullable JLabel myBaseFontInfoLabel; + + private final static int FONT_PANEL_LEFT_OFFSET = 15; public FontOptions(@NotNull ColorAndFontOptions options) { myOptions = options; } @Nullable - protected String getInheritFontTitle() { - return "default font"; + protected String getInheritedFontTitle() { + return "Default font"; + } + + protected String getOverwriteFontTitle() { + return "Set font for color scheme"; } @Override - protected void initControls() { - createInheritCheckBox(); - super.initControls(); + protected JComponent createControls() { + Component inheritBox = createInheritCheckBox(); + if (inheritBox != null) { + JPanel topPanel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.insets = JBUI.insets(BASE_INSET * 2, BASE_INSET, ADDITIONAL_VERTICAL_GAP, 0); + c.anchor = GridBagConstraints.LINE_START; + topPanel.add(inheritBox, c); + c.gridy = 1; + c.gridx = 0; + c.gridwidth = 1; + c.insets = JBUI.emptyInsets(); + topPanel.add(Box.createRigidArea(JBDimension.create(new Dimension(FONT_PANEL_LEFT_OFFSET, 0))), c); + c.gridx = 1; + c.anchor = GridBagConstraints.NORTHWEST; + topPanel.add(createFontSettingsPanel(), c); + return topPanel; + } + else { + return super.createControls(); + } } - private void createInheritCheckBox() { - if (getInheritFontTitle() != null) { + @Nullable + private Component createInheritCheckBox() { + if (getInheritedFontTitle() != null) { JPanel inheritPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0,0 )); inheritPanel.setBorder(BorderFactory.createEmptyBorder()); myInheritFontCheckbox = new JCheckBox(); @@ -68,17 +101,40 @@ public class FontOptions extends AbstractFontOptionsPanel { } }); inheritPanel.add(myInheritFontCheckbox); - inheritPanel.add(new JLabel("Use ")); - inheritPanel.add(createHyperlinkLabel()); - - add(inheritPanel, "newline, span"); - add(new JSeparator(), "newline, growx, span"); + inheritPanel.add(new JLabel(getOverwriteFontTitle())); + inheritPanel.add(Box.createRigidArea(JBDimension.create(new Dimension(10,0)))); + inheritPanel.add(grayed(new JLabel("("))); + inheritPanel.add(grayed(createHyperlinkLabel())); + inheritPanel.add(grayed(new JLabel(": "))); + myBaseFontInfoLabel = grayed(new JLabel("?")); + inheritPanel.add(myBaseFontInfoLabel); + inheritPanel.add(grayed(new JLabel(")"))); + return inheritPanel; } + return null; + } + + private static JLabel grayed(JLabel label) { + label.setForeground(JBColor.GRAY); + return label; + } + + private String getBaseFontInfo() { + StringBuilder sb = new StringBuilder(); + FontPreferences basePrefs = getBaseFontPreferences(); + sb.append(basePrefs.getFontFamily()); + sb.append(','); + sb.append(basePrefs.getSize(basePrefs.getFontFamily())); + return sb.toString(); + } + + protected FontPreferences getBaseFontPreferences() { + return AppEditorFontOptions.getInstance().getFontPreferences(); } @NotNull private JLabel createHyperlinkLabel() { - HoverHyperlinkLabel label = new HoverHyperlinkLabel(getInheritFontTitle()); + HoverHyperlinkLabel label = new HoverHyperlinkLabel(getInheritedFontTitle()); label.addHyperlinkListener(new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent e) { @@ -154,6 +210,9 @@ public class FontOptions extends AbstractFontOptionsPanel { myInheritFontCheckbox.setEnabled(!isReadOnly()); myInheritFontCheckbox.setSelected(isDelegating()); } + if (myBaseFontInfoLabel != null) { + myBaseFontInfoLabel.setText(getBaseFontInfo()); + } } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java index 62be6049a293..533398096195 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java @@ -21,6 +21,7 @@ import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass; import com.intellij.codeInsight.hint.*; import com.intellij.codeInsight.intention.HighPriorityAction; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.IntentionActionDelegate; import com.intellij.codeInsight.intention.impl.config.IntentionActionWrapper; import com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings; import com.intellij.codeInsight.intention.impl.config.IntentionSettingsConfigurable; @@ -479,7 +480,10 @@ public class IntentionHintComponent implements Disposable, ScrollAwareHint { if (source instanceof DataProvider) { final Object selectedItem = PlatformDataKeys.SELECTED_ITEM.getData((DataProvider)source); if (selectedItem instanceof IntentionActionWithTextCaching) { - final IntentionAction action = ((IntentionActionWithTextCaching)selectedItem).getAction(); + IntentionAction action = ((IntentionActionWithTextCaching)selectedItem).getAction(); + if (action instanceof IntentionActionDelegate) { + action = ((IntentionActionDelegate)action).getDelegate(); + } if (action instanceof SuppressIntentionActionFromFix) { if (injectedFile != null && ((SuppressIntentionActionFromFix)action).isShouldBeAppliedToInjectionHost() == ThreeState.NO) { final PsiElement at = injectedFile.findElementAt(injectedEditor.getCaretModel().getOffset()); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/CloseAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/CloseAction.java new file mode 100644 index 000000000000..2e7280024136 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/CloseAction.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2017 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.codeInspection.ui; + +import com.intellij.CommonBundle; +import com.intellij.codeInspection.ex.GlobalInspectionContextImpl; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.DumbAware; + +class CloseAction extends AnAction implements DumbAware { + private GlobalInspectionContextImpl myContext; + + CloseAction(GlobalInspectionContextImpl context) { + super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel); + myContext = context; + } + + @Override + public void actionPerformed(AnActionEvent e) { + myContext.close(true); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java index ef815a16983b..66bc2ec0133a 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java @@ -16,7 +16,6 @@ package com.intellij.codeInspection.ui; -import com.intellij.CommonBundle; import com.intellij.ReviseWhenPortedToJDK; import com.intellij.analysis.AnalysisScope; import com.intellij.analysis.AnalysisUIOptions; @@ -30,7 +29,6 @@ import com.intellij.codeInspection.reference.RefEntity; import com.intellij.codeInspection.ui.actions.ExportHTMLAction; import com.intellij.codeInspection.ui.actions.InvokeQuickFixAction; import com.intellij.diff.util.DiffUtil; -import com.intellij.icons.AllIcons; import com.intellij.ide.*; import com.intellij.ide.actions.ContextHelpAction; import com.intellij.ide.actions.exclusion.ExclusionHandler; @@ -46,7 +44,6 @@ import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.fileEditor.OpenFileDescriptor; -import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.ui.popup.JBPopup; @@ -93,19 +90,14 @@ import java.util.List; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; -/** - * @author max - */ -public class InspectionResultsView extends JPanel implements Disposable, OccurenceNavigator, DataProvider { +public class InspectionResultsView extends JPanel implements Disposable, DataProvider { private static final Logger LOG = Logger.getInstance(InspectionResultsView.class); public static final DataKey DATA_KEY = DataKey.create("inspectionView"); private static final Key PREVIEW_EDITOR_IS_REUSED_KEY = Key.create("inspection.tool.window.preview.editor.is.reused"); - private final Project myProject; private final InspectionTree myTree; - private final ConcurrentMap> myGroups = - ContainerUtil.newConcurrentMap(); + private final ConcurrentMap> myGroups = ContainerUtil.newConcurrentMap(); private final OccurenceNavigator myOccurenceNavigator; private volatile InspectionProfileImpl myInspectionProfile; private final boolean mySettingsEnabled; @@ -144,7 +136,6 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren public InspectionResultsView(@NotNull GlobalInspectionContextImpl globalInspectionContext, @NotNull InspectionRVContentProvider provider) { setLayout(new BorderLayout()); - myProject = globalInspectionContext.getProject(); myInspectionProfile = globalInspectionContext.getCurrentProfile(); myScope = globalInspectionContext.getCurrentScope(); myGlobalInspectionContext = globalInspectionContext; @@ -152,12 +143,12 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren myExcludedInspectionTreeNodesManager = new ExcludedInspectionTreeNodesManager(provider instanceof OfflineInspectionRVContentProvider, isSingleInspectionRun()); - myTree = new InspectionTree(myProject, globalInspectionContext, this); + myTree = new InspectionTree(globalInspectionContext, this); initTreeListeners(); myOccurenceNavigator = initOccurenceNavigator(); - mySplitter = new OnePixelSplitter(false, AnalysisUIOptions.getInstance(myProject).SPLITTER_PROPORTION); + mySplitter = new OnePixelSplitter(false, AnalysisUIOptions.getInstance(globalInspectionContext.getProject()).SPLITTER_PROPORTION); mySplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT)); mySplitter.setHonorComponentsMinimumSize(false); @@ -251,9 +242,9 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren } }; createActionsToolbar(); - PsiManager.getInstance(myProject).addPsiTreeChangeListener(new InspectionViewPsiTreeChangeAdapter(this), this); + PsiManager.getInstance(getProject()).addPsiTreeChangeListener(new InspectionViewPsiTreeChangeAdapter(this), this); - ProjectInspectionProfileManager profileManager = ProjectInspectionProfileManager.getInstance(myProject); + ProjectInspectionProfileManager profileManager = ProjectInspectionProfileManager.getInstance(getProject()); profileManager.addProfileChangeListener(new ProfileChangeAdapter() { @Override public void profileChanged(InspectionProfile profile) { @@ -364,12 +355,9 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren } private void createActionsToolbar() { - final JComponent leftActionsToolbar = createLeftActionsToolbar(); - final JComponent rightActionsToolbar = createRightActionsToolbar(); - JPanel westPanel = new JPanel(new BorderLayout()); - westPanel.add(leftActionsToolbar, BorderLayout.WEST); - westPanel.add(rightActionsToolbar, BorderLayout.EAST); + westPanel.add(createLeftActionsToolbar(), BorderLayout.WEST); + westPanel.add(createRightActionsToolbar(), BorderLayout.EAST); add(westPanel, BorderLayout.WEST); } @@ -389,8 +377,8 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren private JComponent createLeftActionsToolbar() { final CommonActionsManager actionsManager = CommonActionsManager.getInstance(); DefaultActionGroup group = new DefaultActionGroup(); - group.add(new RerunAction(this)); - group.add(new CloseAction()); + group.add(new RerunAction(this, this)); + group.add(new CloseAction(myGlobalInspectionContext)); final TreeExpander treeExpander = new DefaultTreeExpander(myTree); group.add(actionsManager.createExpandAllAction(treeExpander, myTree)); group.add(actionsManager.createCollapseAllAction(treeExpander, myTree)); @@ -419,9 +407,8 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren } } - private boolean isAutoScrollMode() { - String activeToolWindowId = ToolWindowManager.getInstance(myProject).getActiveToolWindowId(); + String activeToolWindowId = ToolWindowManager.getInstance(getProject()).getActiveToolWindowId(); return myGlobalInspectionContext.getUIOptions().AUTOSCROLL_TO_SOURCE && (activeToolWindowId == null || activeToolWindowId.equals(ToolWindowId.INSPECTION)); } @@ -436,27 +423,13 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren @Nullable private static OpenFileDescriptor getOpenFileDescriptor(final RefElement refElement) { - final VirtualFile[] file = new VirtualFile[1]; - final int[] offset = new int[1]; - - ApplicationManager.getApplication().runReadAction(() -> { - PsiElement psiElement = refElement.getElement(); - if (psiElement != null) { - final PsiFile containingFile = psiElement.getContainingFile(); - if (containingFile != null) { - file[0] = containingFile.getVirtualFile(); - offset[0] = psiElement.getTextOffset(); - } - } - else { - file[0] = null; - } - }); - - if (file[0] != null && file[0].isValid()) { - return new OpenFileDescriptor(refElement.getRefManager().getProject(), file[0], offset[0]); - } - return null; + PsiElement psiElement = refElement.getElement(); + if (psiElement == null) return null; + final PsiFile containingFile = psiElement.getContainingFile(); + if (containingFile == null) return null; + VirtualFile file = containingFile.getVirtualFile(); + if (file == null) return null; + return new OpenFileDescriptor(refElement.getRefManager().getProject(), file, psiElement.getTextOffset()); } public void setApplyingFix(boolean applyingFix) { @@ -601,7 +574,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren myPreviewEditor.getMarkupModel().removeAllHighlighters(); } else { - myPreviewEditor = (EditorEx)EditorFactory.getInstance().createEditor(document, myProject, file.getVirtualFile(), true); + myPreviewEditor = (EditorEx)EditorFactory.getInstance().createEditor(document, getProject(), file.getVirtualFile(), true); DiffUtil.setFoldingModelSupport(myPreviewEditor); final EditorSettings settings = myPreviewEditor.getSettings(); settings.setLineNumbersShown(false); @@ -621,7 +594,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren return Pair.create(myPreviewEditor.getComponent(), myPreviewEditor); } if (selectedEntity == null) { - return Pair.create(new InspectionNodeInfo(myTree, myProject), null); + return Pair.create(new InspectionNodeInfo(myTree, getProject()), null); } if (selectedEntity.isValid()) { return Pair.create(InspectionResultsViewUtil.getPreviewIsNotAvailable(selectedEntity), null); @@ -790,7 +763,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren InspectionToolWrapper toolWrapper = state.getTool(); if (ReadAction.compute(() -> myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper))) { addTool(toolWrapper, - profile.getErrorLevel(key, state.getScope(myProject), myProject), + profile.getErrorLevel(key, state.getScope(getProject()), getProject()), isGroupedBySeverity, singleInspectionRun); } @@ -849,7 +822,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren if (isGroupedBySeverity) { InspectionSeverityGroupNode severityGroupNode = mySeverityGroupNodes.get(level); if (severityGroupNode == null) { - InspectionSeverityGroupNode newNode = new InspectionSeverityGroupNode(myProject, level); + InspectionSeverityGroupNode newNode = new InspectionSeverityGroupNode(getProject(), level); severityGroupNode = ConcurrencyUtil.cacheOrGet(mySeverityGroupNodes, level, newNode); if (severityGroupNode == newNode) { InspectionTreeNode root = myTree.getRoot(); @@ -865,39 +838,9 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren return myOccurenceNavigator; } - @Override - public boolean hasNextOccurence() { - return myOccurenceNavigator != null && myOccurenceNavigator.hasNextOccurence(); - } - - @Override - public boolean hasPreviousOccurence() { - return myOccurenceNavigator != null && myOccurenceNavigator.hasPreviousOccurence(); - } - - @Override - public OccurenceInfo goNextOccurence() { - return myOccurenceNavigator != null ? myOccurenceNavigator.goNextOccurence() : null; - } - - @Override - public OccurenceInfo goPreviousOccurence() { - return myOccurenceNavigator != null ? myOccurenceNavigator.goPreviousOccurence() : null; - } - - @Override - public String getNextOccurenceActionName() { - return myOccurenceNavigator != null ? myOccurenceNavigator.getNextOccurenceActionName() : ""; - } - - @Override - public String getPreviousOccurenceActionName() { - return myOccurenceNavigator != null ? myOccurenceNavigator.getPreviousOccurenceActionName() : ""; - } - @NotNull public Project getProject() { - return myProject; + return myGlobalInspectionContext.getProject(); } @Override @@ -1008,7 +951,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren startOffset = textRange.getStartOffset(); } } - return new OpenFileDescriptor(myProject, virtualFile, startOffset); + return new OpenFileDescriptor(getProject(), virtualFile, startOffset); } return null; } @@ -1107,44 +1050,11 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren return myDisposed; } - private class CloseAction extends AnAction implements DumbAware { - private CloseAction() { - super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel); - } - - @Override - public void actionPerformed(AnActionEvent e) { - myGlobalInspectionContext.close(true); - } - } - public void updateCurrentProfile() { final String name = myInspectionProfile.getName(); myInspectionProfile = myInspectionProfile.getProfileManager().getProfile(name); } - private class RerunAction extends AnAction { - RerunAction(JComponent comp) { - super(InspectionsBundle.message("inspection.action.rerun"), InspectionsBundle.message("inspection.action.rerun"), - AllIcons.Actions.Rerun); - registerCustomShortcutSet(CommonShortcuts.getRerun(), comp); - } - - @Override - public void update(AnActionEvent e) { - e.getPresentation().setEnabled(isRerunAvailable()); - } - - @Override - public void actionPerformed(AnActionEvent e) { - rerun(); - } - - private void rerun() { - InspectionResultsView.this.rerun(); - } - } - public boolean isRerunAvailable() { return !(myProvider instanceof OfflineInspectionRVContentProvider) && myScope.isValid(); } @@ -1152,7 +1062,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren public void rerun() { myRerun = true; if (myScope.isValid()) { - AnalysisUIOptions.getInstance(myProject).save(myGlobalInspectionContext.getUIOptions()); + AnalysisUIOptions.getInstance(getProject()).save(myGlobalInspectionContext.getUIOptions()); myGlobalInspectionContext.setTreeState(getTree().getTreeState()); myGlobalInspectionContext.doInspections(myScope); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java index a08fffb23132..90dccde763d4 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java @@ -56,9 +56,9 @@ public class InspectionTree extends Tree { @NotNull private InspectionTreeState myState = new InspectionTreeState(); private boolean myQueueUpdate; - public InspectionTree(@NotNull Project project, - @NotNull GlobalInspectionContextImpl context, + public InspectionTree(@NotNull GlobalInspectionContextImpl context, @NotNull InspectionResultsView view) { + Project project = context.getProject(); setModel(new DefaultTreeModel(new InspectionRootNode(project, new InspectionTreeUpdater(view)))); myContext = context; myExcludedManager = view.getExcludedManager(); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeState.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeState.java index 70a850707b5e..bf420dfb96a2 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeState.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeState.java @@ -63,14 +63,14 @@ public class InspectionTreeState { private static class InspectionTreeSelectionPath { private final Object[] myPath; - private final int[] myIndicies; + private final int[] myIndices; public InspectionTreeSelectionPath(TreePath path) { myPath = path.getPath(); - myIndicies = new int[myPath.length]; + myIndices = new int[myPath.length]; for (int i = 0; i < myPath.length - 1; i++) { InspectionTreeNode node = (InspectionTreeNode)myPath[i]; - myIndicies[i + 1] = getChildIndex(node, (InspectionTreeNode)myPath[i + 1]); + myIndices[i + 1] = getChildIndex(node, (InspectionTreeNode)myPath[i + 1]); } } @@ -116,8 +116,8 @@ public class InspectionTreeState { // Exactly same element not found. Trying to select somewhat near. int count = newRoot.getChildCount(); if (count > 0) { - if (myIndicies[idx] < count) { - newPath.add(newRoot.getChildAt(myIndicies[idx])); + if (myIndices[idx] < count) { + newPath.add(newRoot.getChildAt(myIndices[idx])); } else { newPath.add(newRoot.getChildAt(count - 1)); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/RerunAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/RerunAction.java new file mode 100644 index 000000000000..2224c6a19cbd --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/RerunAction.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2017 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.codeInspection.ui; + +import com.intellij.codeInspection.InspectionsBundle; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonShortcuts; + +import javax.swing.*; + +class RerunAction extends AnAction { + private final InspectionResultsView myView; + + RerunAction(InspectionResultsView view, JComponent comp) { + super(InspectionsBundle.message("inspection.action.rerun"), InspectionsBundle.message("inspection.action.rerun"), + AllIcons.Actions.Rerun); + myView = view; + registerCustomShortcutSet(CommonShortcuts.getRerun(), comp); + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(myView.isRerunAvailable()); + } + + @Override + public void actionPerformed(AnActionEvent e) { + rerun(); + } + + private void rerun() { + myView.rerun(); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/RunDashboardManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/dashboard/RunDashboardManagerImpl.java index af5b22912b1b..7d35ab3fd4ed 100644 --- a/platform/lang-impl/src/com/intellij/execution/dashboard/RunDashboardManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/RunDashboardManagerImpl.java @@ -376,6 +376,11 @@ public class RunDashboardManagerImpl implements RunDashboardManager, PersistentS .map(content -> Pair.create(content.getDisplayName(), content.getComponent())).collect(Collectors.toList()); } + @Override + public boolean hasMultipleTabs() { + return myDashboardContentManager.getContents().length > 1; + } + @Override public String getTitlePrefix() { return myTitlePrefix; diff --git a/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java b/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java index 19287539275c..8db1e77d0ed4 100644 --- a/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/IndentImpl.java @@ -50,7 +50,7 @@ public class IndentImpl extends Indent { /** * @return {@code 'isAbsolute'} property value as defined during {@link IndentImpl} object construction */ - boolean isAbsolute(){ + public boolean isAbsolute() { return myIsAbsolute; } diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java index c46db97f7546..a991bb85fafe 100644 --- a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java @@ -289,7 +289,7 @@ public class ScratchFileServiceImpl extends ScratchFileService implements Persis return VfsUtil.createChildSequent(LocalFileSystem.getInstance(), dir, fileName, StringUtil.notNullize(ext)); } else { - return dir.createChildData(LocalFileSystem.getInstance(), fileNameExt); + return dir.findOrCreateChildData(LocalFileSystem.getInstance(), fileNameExt); } }); } diff --git a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java index 709ad66242e6..c1472c420881 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java +++ b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java @@ -58,6 +58,7 @@ public class UnsafeUsagesDialog extends DialogWrapper { JPanel panel = new JPanel(new BorderLayout()); myMessagePane = new JEditorPane(UIUtil.HTML_MIME, ""); myMessagePane.setEditable(false); + myMessagePane.setEditorKit(UIUtil.getHTMLEditorKit()); JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myMessagePane); scrollPane.setPreferredSize(JBUI.size(500, 400)); panel.add(new JLabel(RefactoringBundle.message("the.following.problems.were.found")), BorderLayout.NORTH); diff --git a/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java b/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java index 62fca11394a4..167a6dd85b98 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java @@ -110,7 +110,7 @@ public abstract class PanelWithActionsAndCloseButton extends JPanel implements D Content content = myContentManager.getContent(PanelWithActionsAndCloseButton.this); if (content != null) { ContentsUtil.closeContentTab(myContentManager, content); - if (content instanceof TabbedContent && ((TabbedContent)content).getTabs().size() > 1) { + if (content instanceof TabbedContent && ((TabbedContent)content).hasMultipleTabs()) { final TabbedContent tabbedContent = (TabbedContent)content; final JComponent component = content.getComponent(); tabbedContent.removeContent(component); diff --git a/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java b/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java index f3606dac8238..4d5ad49911e6 100644 --- a/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java +++ b/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -29,10 +29,33 @@ public interface TabbedContent extends Content { String SPLIT_PROPERTY_PREFIX = "tabbed.toolwindow.expanded."; void addContent(@NotNull JComponent content, @NotNull String name, boolean selectTab); + void removeContent(@NotNull JComponent content); + + /** + * This method is used for preselecting popup menu items + * + * @return index of selected tab + * @see #selectContent(int) + */ + default int getSelectedIndex() { return -1; } + + /** + * This method is invoked before content is selected with {@link ContentManager#setSelectedContent(Content)} + * + * @param index index of tab in {@link #getTabs()} + */ void selectContent(int index); + List> getTabs(); + + default boolean hasMultipleTabs() { + return getTabs().size() > 1; + } + String getTitlePrefix(); + void setTitlePrefix(String titlePrefix); + void split(); } diff --git a/platform/platform-api/src/com/intellij/util/ContentsUtil.java b/platform/platform-api/src/com/intellij/util/ContentsUtil.java index 0595becc14eb..550c980344a7 100644 --- a/platform/platform-api/src/com/intellij/util/ContentsUtil.java +++ b/platform/platform-api/src/com/intellij/util/ContentsUtil.java @@ -58,7 +58,7 @@ public class ContentsUtil { public static void closeContentTab(@NotNull ContentManager contentManager, @NotNull Content content) { if (content instanceof TabbedContent) { TabbedContent tabbedContent = (TabbedContent)content; - if (tabbedContent.getTabs().size() > 1) { + if (tabbedContent.hasMultipleTabs()) { JComponent component = tabbedContent.getComponent(); tabbedContent.removeContent(component); contentManager.setSelectedContent(tabbedContent, true, true); diff --git a/platform/platform-api/src/com/intellij/util/net/IOExceptionDialog.java b/platform/platform-api/src/com/intellij/util/net/IOExceptionDialog.java index 571ab8edc263..35dc36354d77 100644 --- a/platform/platform-api/src/com/intellij/util/net/IOExceptionDialog.java +++ b/platform/platform-api/src/com/intellij/util/net/IOExceptionDialog.java @@ -41,7 +41,6 @@ public class IOExceptionDialog extends DialogWrapper { myErrorLabel = new JTextArea(); myErrorLabel.setEditable(false); myErrorLabel.setText(errorText); - myErrorLabel.setColumns(120); myErrorLabel.setLineWrap(true); myErrorLabel.setWrapStyleWord(true); myErrorLabel.setFont(UIManager.getFont("Label.font")); diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/JdkSettingsUsageCollector.kt b/platform/platform-impl/src/com/intellij/internal/statistic/JdkSettingsUsageCollector.kt index 81e268480da9..aab4a81d85b7 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/JdkSettingsUsageCollector.kt +++ b/platform/platform-impl/src/com/intellij/internal/statistic/JdkSettingsUsageCollector.kt @@ -25,10 +25,23 @@ import java.lang.management.ManagementFactory class JdkSettingsUsageCollector: UsagesCollector() { override fun getUsages(): Set { return ManagementFactory.getRuntimeMXBean().inputArguments + .map { s -> hideUserPath(s) } .map { s -> UsageDescriptor(s) } .toSet() } + val keysWithPath = arrayOf("-Didea.home.path", "-Didea.launcher.bin.path", "-Didea.plugins.path", "-Xbootclasspath", + "-Djb.vmOptionsFile", "-XX ErrorFile", "-XX HeapDumpPath", " -Didea.launcher.bin.path", "-agentlib:jdwp") + + private fun hideUserPath(key: String): String { + @Suppress("LoopToCallChain") + for (s in keysWithPath) { + if (key.startsWith(s)) return "$s ..." + } + + return key + } + override fun getGroupId(): GroupDescriptor { return GroupDescriptor.create("user.jdk.settings") } diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java b/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java new file mode 100644 index 000000000000..dafff7b6655f --- /dev/null +++ b/platform/platform-impl/src/com/intellij/internal/statistic/ShortcutsCollector.java @@ -0,0 +1,117 @@ +/* + * Copyright 2000-2017 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.internal.statistic; + +import com.intellij.internal.statistic.beans.GroupDescriptor; +import com.intellij.internal.statistic.beans.UsageDescriptor; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.KeyboardShortcut; +import com.intellij.openapi.components.*; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.xmlb.annotations.MapAnnotation; +import com.intellij.util.xmlb.annotations.Tag; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author Konstantin Bulenkov + */ +@State( + name = "ShortcutsCollector", + storages = @Storage(value = "statistics.shortcuts.xml", roamingType = RoamingType.DISABLED) +) +public class ShortcutsCollector implements PersistentStateComponent { + final static class MyState { + @Tag("counts") + @MapAnnotation(surroundWithTag = false, keyAttributeName = "shortcut", valueAttributeName = "count") + public final Map myValues = new HashMap<>(); + } + private MyState myState = new MyState(); + + @NotNull + public MyState getState() { + return myState; + } + + public void loadState(final MyState state) { + myState = state; + } + + public static void record(AnActionEvent event) { + _record(event, false); + } + + public static void recordDoubleShortcut(AnActionEvent event) { + _record(event, true); + } + + private static void _record(AnActionEvent event, boolean isDoubleShortcut) { + InputEvent e = event.getInputEvent(); + if (e instanceof KeyEvent) { + KeyboardShortcut shortcut = new KeyboardShortcut(KeyStroke.getKeyStrokeForEvent((KeyEvent)e), null); + String key = KeymapUtil.getShortcutText(shortcut); + if (isDoubleShortcut) { + key = SystemInfo.isMac ? key + key : key + "+" + key; + } + incValue(key); + } + } + + private static void incValue(String key) { + ShortcutsCollector collector = getInstance(); + + if (collector == null) return; //no shortcuts stats for the IDE + + Map values = collector.getState().myValues; + values.put(key, ContainerUtil.getOrElse(values, key, 0) + 1); + } + + + private static ShortcutsCollector getInstance() { + return ServiceManager.getService(ShortcutsCollector.class); + } + + final static class ShortcutUsagesCollector extends UsagesCollector { + private static final GroupDescriptor GROUP = GroupDescriptor.create(getGroupName(), GroupDescriptor.HIGHER_PRIORITY); + + private static String getGroupName() { + if (SystemInfo.isMac) return "Shortcuts on Mac"; + if (SystemInfo.isWindows) return "Shortcuts on Windows"; + if (SystemInfo.isLinux) return "Shortcuts on Linux"; + return "Shortcuts on OtherOs"; + } + + @NotNull + public Set getUsages() { + MyState state = getInstance().getState(); + assert state != null; + return ContainerUtil.map2Set(state.myValues.entrySet(), e -> new UsageDescriptor(e.getKey(), e.getValue())); + } + + @NotNull + public GroupDescriptor getGroupId() { + return GROUP; + } + } +} diff --git a/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java b/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java index 1e688f143a56..a98262b8a1a8 100644 --- a/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java @@ -325,10 +325,8 @@ public class NotificationsManagerImpl extends NotificationsManager { @NotNull Disposable parentDisposable) { final BalloonLayoutData layoutData = layoutDataRef.isNull() ? new BalloonLayoutData() : layoutDataRef.get(); if (layoutData.groupId == null) { - if (NotificationsConfigurationImpl.getSettings(notification.getGroupId()).isShouldLog()) { - layoutData.groupId = notification.getGroupId(); - layoutData.id = notification.id; - } + layoutData.groupId = notification.getGroupId(); + layoutData.id = notification.id; } else { layoutData.groupId = null; diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java index eb72386668d9..2585c2009b33 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java @@ -19,6 +19,7 @@ import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; import com.intellij.ide.ProhibitAWTEvents; import com.intellij.ide.impl.DataManagerImpl; +import com.intellij.internal.statistic.ShortcutsCollector; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; @@ -562,6 +563,7 @@ public final class IdeKeyEventDispatcher implements Disposable { @Override public void performAction(@NotNull InputEvent e, @NotNull AnAction action, @NotNull AnActionEvent actionEvent) { e.consume(); + ShortcutsCollector.record(actionEvent); DataContext ctx = actionEvent.getDataContext(); if (action instanceof ActionGroup && !((ActionGroup)action).canBePerformed(ctx)) { diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java index a70a78f118a8..756b060dca8c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java @@ -17,6 +17,7 @@ package com.intellij.openapi.keymap.impl; import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; +import com.intellij.internal.statistic.ShortcutsCollector; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; @@ -260,6 +261,7 @@ public class ModifierKeyDoubleClickHandler implements Disposable, ApplicationCom myActionManagerEx.fireBeforeActionPerformed(action, anActionEvent.getDataContext(), anActionEvent); action.actionPerformed(anActionEvent); myActionManagerEx.fireAfterActionPerformed(action, anActionEvent.getDataContext(), anActionEvent); + ShortcutsCollector.recordDoubleShortcut(anActionEvent); return true; } finally { diff --git a/platform/platform-impl/src/com/intellij/openapi/util/WindowStateServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/util/WindowStateServiceImpl.java index 67d2c9eb27ad..622e5231de38 100644 --- a/platform/platform-impl/src/com/intellij/openapi/util/WindowStateServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/util/WindowStateServiceImpl.java @@ -26,11 +26,13 @@ import com.intellij.util.ui.UIUtil; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.Map; import java.util.TreeMap; -import java.util.function.Supplier; +import java.util.function.BiFunction; +import java.util.function.Function; /** * @author Sergey.Malenkov @@ -219,9 +221,32 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers private T getFor(Object object, @NotNull String key, @NotNull Class type) { GraphicsDevice screen = getScreen(object); - T state = get(getKey(screen, key), type); + float scale = getSysScale(screen); + + Function getState = (myKey) -> { + WindowState state = myStateMap.get(myKey); + if (state == null) return null; + state = state.copy().scaleDown(scale); + if (isVisible(state)) { + if (type == WindowState.class) { + return (T)state; + } + if (type == Point.class) { + return (T)state.getLocation(); + } + if (type == Dimension.class) { + return (T)state.getSize(); + } + if (type == Rectangle.class) { + return (T)state.getBounds(); + } + } + return null; + }; + + T state = getState.apply(getKey(screen, key)); if (state == null) { - state = get(getOldKey(screen, key), type); + state = getState.apply(getOldKey(screen, key)); } if (state != null) { return state; @@ -229,29 +254,7 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers if (object != null) { return getFor(null, key, type); } - return get(new KeyPair(key, 1f), type); - } - - @SuppressWarnings("unchecked") - private T get(@NotNull KeyPair keyPair, @NotNull Class type) { - WindowState state = myStateMap.get(keyPair.first); - if (state == null) return null; - state = state.copy().scaleDown(keyPair.second); - if (isVisible(state)) { - if (type == WindowState.class) { - return (T)state; - } - if (type == Point.class) { - return (T)state.getLocation(); - } - if (type == Dimension.class) { - return (T)state.getSize(); - } - if (type == Rectangle.class) { - return (T)state.getBounds(); - } - } - return null; + return getState.apply(key); } private void putFor(Object object, @NotNull String key, @@ -261,63 +264,64 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers boolean fullScreen, boolean fullScreenSet) { synchronized (myStateMap) { GraphicsDevice screen = getScreen(object); - KeyPair oldKeyPair = getOldKey(screen, key); + float scale = getSysScale(screen); - putImpl(getKey(screen, key), oldKeyPair, location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet); + BiFunction putState = (myNewKey, myOldKey) -> { + // remove & migrate the old key state + WindowState oldState = myOldKey != null ? myStateMap.remove(myOldKey) : null; + if (oldState != null) { + oldState.scaleDown(scale); + WindowState newState = myStateMap.get(myNewKey); + if (newState != null) { + newState.merge(oldState); + } else { + myStateMap.put(myNewKey, oldState); + } + } + // put the new key state + WindowState state = myStateMap.get(myNewKey); + if (state != null) { + if (state.set(location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet)) { + state.scaleUp(scale); + } else { + myStateMap.remove(myNewKey); + } + } + else { + state = new WindowState(); + if (state.set(location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet)) { + state.scaleUp(scale); + myStateMap.put(myNewKey, state); + } + } + return null; + }; + + putState.apply(getKey(screen, key), getOldKey(screen, key)); if (screen != null) { - putImpl(getKey(null, key), oldKeyPair, location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet); + putState.apply(getKey(null, key), getOldKey(null, key)); } - putImpl(new KeyPair(key, 1f), oldKeyPair, location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet); + putState.apply(key, null); } } - private void putImpl(@NotNull KeyPair keyPair, - @NotNull KeyPair oldKeyPair, - Point location, boolean locationSet, - Dimension size, boolean sizeSet, - boolean maximized, boolean maximizedSet, - boolean fullScreen, boolean fullScreenSet) { - WindowState state = myStateMap.get(keyPair.first); - - // may be convert the old key state to the new key - WindowState oldState = myStateMap.remove(oldKeyPair.first); - if (oldState != null) { - oldState.scaleDown(oldKeyPair.second); - if (state != null) { - state.merge(oldState); - } else { - myStateMap.put(keyPair.first, state = oldState); - } - } - if (state != null) { - if (state.set(location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet)) { - state.scaleUp(keyPair.second); - } else { - myStateMap.remove(keyPair.first); - } - } - else { - state = new WindowState(); - if (state.set(location, locationSet, size, sizeSet, maximized, maximizedSet, fullScreen, fullScreenSet)) { - state.scaleUp(keyPair.second); - myStateMap.put(keyPair.first, state); - } - } + private static float getSysScale(GraphicsDevice screen) { + return UIUtil.isJreHiDPIEnabled() && screen != null ? JBUI.sysScale(screen.getDefaultConfiguration()) : 1f; } /* * todo: old hidpi-unaware key; to be removed */ @NotNull - private static KeyPair getOldKey(GraphicsDevice screen, String key) { + private static String getOldKey(@Nullable GraphicsDevice screen, String key) { GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); if (environment.isHeadlessInstance()) { - return new KeyPair(key + ".headless", 1f); + return key + ".headless"; } StringBuilder sb = new StringBuilder(key); - float scale = 1f; for (GraphicsDevice device : environment.getScreenDevices()) { Rectangle bounds = device.getDefaultConfiguration().getBounds(); + normalizeSize(device, bounds); sb.append('/').append(bounds.x); sb.append('.').append(bounds.y); sb.append('.').append(bounds.width); @@ -325,25 +329,22 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers } if (screen != null) { Rectangle bounds = screen.getDefaultConfiguration().getBounds(); + normalizeSize(screen, bounds); sb.append('@').append(bounds.x); sb.append('.').append(bounds.y); sb.append('.').append(bounds.width); sb.append('.').append(bounds.height); - if (UIUtil.isJreHiDPIEnabled()) { - scale = JBUI.sysScale(screen.getDefaultConfiguration()); - } } - return new KeyPair(sb.toString(), scale); + return sb.toString(); } @NotNull - private static KeyPair getKey(GraphicsDevice screen, String key) { + private static String getKey(@Nullable GraphicsDevice screen, String key) { GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); if (environment.isHeadlessInstance()) { - return new KeyPair(key + ".headless", 1f); + return key + ".headless"; } StringBuilder sb = new StringBuilder(key); - float scale = 1f; // not storing screen x,y due to relying on isVisible(state) on key retrieval if (screen == null) { for (GraphicsDevice device : environment.getScreenDevices()) { @@ -360,13 +361,10 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers sb.append('.').append(bounds.height); if (JBUI.isPixHiDPI(screen.getDefaultConfiguration())) { int dpi = ((int)(96 * JBUI.pixScale(screen.getDefaultConfiguration()))); - sb.append("@" + dpi + "dpi"); - } - if (UIUtil.isJreHiDPIEnabled()) { - scale = JBUI.sysScale(screen.getDefaultConfiguration()); + sb.append("@").append(dpi).append("dpi"); } } - return new KeyPair(sb.toString(), scale); + return sb.toString(); } private static void normalizeSize(GraphicsDevice screen, Rectangle bounds) { @@ -390,15 +388,13 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers } if (object instanceof Window) { Window window = (Window)object; - object = ScreenUtil.getScreenDevice(window.getBounds()); - if (object == null) { - LOG.warn("cannot find a screen for " + window); - return null; + GraphicsConfiguration gc = window.getGraphicsConfiguration(); + GraphicsDevice device = gc != null ? + window.getGraphicsConfiguration().getDevice() : ScreenUtil.getScreenDevice(window.getBounds()); + if (device != null) { + return device; } } - if (object instanceof GraphicsDevice) { - return (GraphicsDevice)object; - } LOG.warn("cannot find a screen for " + object); return null; } @@ -492,10 +488,4 @@ abstract class WindowStateServiceImpl extends WindowStateService implements Pers } return ScreenUtil.isVisible(new Rectangle(location, size)); } - - private static class KeyPair extends Pair { - public KeyPair(String key, Float scale) { - super(key, scale); - } - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java index 4c4f7598b1ef..13abb7baaff1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -23,6 +23,7 @@ import com.intellij.util.ui.BaseButtonBehavior; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.TimedDeadzone; import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -30,26 +31,27 @@ import java.awt.event.MouseEvent; class ContentTabLabel extends BaseLabel { - Content myContent; - private final BaseButtonBehavior myBehavior; + private final Content myContent; private final TabContentLayout myLayout; - public ContentTabLabel(final Content content, TabContentLayout layout) { + public ContentTabLabel(@NotNull Content content, @NotNull TabContentLayout layout) { super(layout.myUi, true); myLayout = layout; myContent = content; - update(); - - myBehavior = new BaseButtonBehavior(this) { + BaseButtonBehavior behavior = new BaseButtonBehavior(this) { protected void execute(final MouseEvent e) { - final ContentManager mgr = contentManager(); - if (mgr.getIndexOfContent(myContent) >= 0) { - mgr.setSelectedContent(myContent, true); - } + selectContent(); } }; - myBehavior.setActionTrigger(MouseEvent.MOUSE_PRESSED); - myBehavior.setMouseDeadzone(TimedDeadzone.NULL); + behavior.setActionTrigger(MouseEvent.MOUSE_PRESSED); + behavior.setMouseDeadzone(TimedDeadzone.NULL); + } + + protected void selectContent() { + final ContentManager mgr = contentManager(); + if (mgr.getIndexOfContent(myContent) >= 0) { + mgr.setSelectedContent(myContent, true); + } } public void update() { @@ -106,6 +108,7 @@ class ContentTabLabel extends BaseLabel { return myUi.myWindow.getContentManager(); } + @NotNull @Override public Content getContent() { return myContent; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentStep.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentStep.kt new file mode 100644 index 000000000000..7c4e82093e41 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentStep.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2017 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.wm.impl.content + +import com.intellij.openapi.ui.popup.PopupStep +import com.intellij.openapi.ui.popup.util.BaseListPopupStep +import com.intellij.ui.content.Content +import com.intellij.ui.content.TabbedContent +import javax.swing.Icon + +class SelectContentStep : BaseListPopupStep { + + constructor(contents: Array) : super(null, *contents) + constructor(contents: List) : super(null, contents) + + override fun isSpeedSearchEnabled(): Boolean = true + + override fun getIconFor(value: Content): Icon? = value.icon + + override fun getTextFor(value: Content): String { + return value.asMultiTabbed()?.titlePrefix ?: value.displayName ?: super.getTextFor(value) + } + + override fun hasSubstep(value: Content): Boolean = value.asMultiTabbed() != null + + override fun onChosen(value: Content, finalChoice: Boolean): PopupStep<*>? { + val tabbed = value.asMultiTabbed() + if (tabbed == null) { + value.manager?.setSelectedContentCB(value, true, true) + return PopupStep.FINAL_CHOICE + } + else { + return SelectContentTabStep(tabbed) + } + } + + private fun Content.asMultiTabbed(): TabbedContent? = if (this is TabbedContent && hasMultipleTabs()) this else null +} diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentTabStep.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentTabStep.kt new file mode 100644 index 000000000000..762f3cbb519e --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/SelectContentTabStep.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2017 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.wm.impl.content + +import com.intellij.openapi.ui.popup.PopupStep +import com.intellij.openapi.ui.popup.util.BaseListPopupStep +import com.intellij.ui.content.TabbedContent + +class SelectContentTabStep(val content: TabbedContent) : BaseListPopupStep(null) { + + private val myTabs = content.tabs + + init { + val indexes = (0 until myTabs.size).toList() + init(null, indexes, null) + defaultOptionIndex = content.selectedIndex + } + + override fun isSpeedSearchEnabled(): Boolean = true + + override fun getTextFor(value: Int): String = myTabs[value].first + + override fun onChosen(selectedValue: Int, finalChoice: Boolean): PopupStep<*>? { + val manager = content.manager ?: return FINAL_CHOICE + content.selectContent(selectedValue) + manager.setSelectedContent(content) + return FINAL_CHOICE + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java index ca060eead771..b0b68e87e382 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -17,38 +17,31 @@ package com.intellij.openapi.wm.impl.content; import com.intellij.ide.dnd.DnDSupport; import com.intellij.ide.dnd.DnDTarget; -import com.intellij.openapi.ui.JBPopupMenu; +import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.ui.UIBundle; +import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.awt.RelativeRectangle; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManager; import com.intellij.ui.content.ContentManagerEvent; import com.intellij.ui.content.TabbedContent; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.BaseButtonBehavior; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.List; class TabContentLayout extends ContentLayout { static final int MORE_ICON_BORDER = 6; LayoutData myLastLayout; - JPopupMenu myPopup; - final PopupMenuListener myPopupListener; - ArrayList myTabs = new ArrayList<>(); final Map myContent2Tabs = new HashMap<>(); @@ -71,8 +64,6 @@ class TabContentLayout extends ContentLayout { TabContentLayout(ToolWindowContentUi ui) { super(ui); - myPopupListener = new MyPopupListener(); - new BaseButtonBehavior(myUi) { protected void execute(final MouseEvent e) { if (!myUi.isCurrent(TabContentLayout.this)) return; @@ -80,7 +71,7 @@ class TabContentLayout extends ContentLayout { if (myLastLayout != null) { final Rectangle moreRect = myLastLayout.moreRect; if (moreRect != null && moreRect.contains(e.getPoint())) { - showPopup(); + showPopup(e, ContainerUtil.filter(myTabs, myLastLayout.toDrop::contains)); } } } @@ -109,41 +100,10 @@ class TabContentLayout extends ContentLayout { myIdLabel = null; } - private void showPopup() { - myPopup = new JBPopupMenu(); - myPopup.addPopupMenuListener(myPopupListener); - - ArrayList tabs = myTabs; - - for (final ContentTabLabel each : tabs) { - final JCheckBoxMenuItem item = new JCheckBoxMenuItem(each.getText()); - if (myUi.myManager.isSelected(each.myContent)) { - item.setSelected(true); - } - item.addActionListener(new ActionListener() { - public void actionPerformed(final ActionEvent e) { - myUi.myManager.setSelectedContent(each.myContent, true); - } - }); - myPopup.add(item); - } - myPopup.show(myUi, myLastLayout.moreRect.x, myLastLayout.moreRect.y); - } - - - private class MyPopupListener implements PopupMenuListener { - public void popupMenuWillBecomeVisible(final PopupMenuEvent e) { - } - - public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) { - if (myPopup != null) { - myPopup.removePopupMenuListener(this); - } - myPopup = null; - } - - public void popupMenuCanceled(final PopupMenuEvent e) { - } + private static void showPopup(MouseEvent e, List tabs) { + final List contentsToShow = ContainerUtil.map(tabs, ContentTabLabel::getContent); + final SelectContentStep step = new SelectContentStep(contentsToShow); + JBPopupFactory.getInstance().createListPopup(step).show(new RelativePoint(e)); } @Override @@ -173,7 +133,7 @@ class TabContentLayout extends ContentLayout { myLastLayout.contentCount == manager.getContentCount()) { for (ContentTabLabel each : myTabs) { if (!each.isValid()) break; - if (each.myContent == selected && each.getBounds().width != 0) { + if (each.getContent() == selected && each.getBounds().width != 0) { data = myLastLayout; data.fullLayout = false; } @@ -294,7 +254,7 @@ class TabContentLayout extends ContentLayout { int moreRectWidth; ArrayList toLayout = new ArrayList<>(); - ArrayList toDrop = new ArrayList<>(); + Collection toDrop = new HashSet<>(); Rectangle moreRect; @@ -429,11 +389,11 @@ class TabContentLayout extends ContentLayout { myUi.removeAll(); myUi.add(myIdLabel); - myUi.initMouseListeners(myIdLabel, myUi); + ToolWindowContentUi.initMouseListeners(myIdLabel, myUi); for (ContentTabLabel each : myTabs) { myUi.add(each); - myUi.initMouseListeners(each, myUi); + ToolWindowContentUi.initMouseListeners(each, myUi); } myCached.clear(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java index 7537e337e5ba..84967d1318fc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -18,23 +18,15 @@ package com.intellij.openapi.wm.impl.content; import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Pair; import com.intellij.reference.SoftReference; -import com.intellij.ui.ClickListener; -import com.intellij.ui.components.JBList; import com.intellij.ui.content.TabbedContent; -import com.intellij.util.NotNullFunction; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; -import javax.swing.*; -import javax.swing.border.EmptyBorder; import java.awt.*; -import java.awt.event.MouseEvent; import java.lang.ref.Reference; import java.lang.ref.WeakReference; -import java.util.ArrayList; /** * @author Konstantin Bulenkov @@ -54,45 +46,23 @@ public class TabbedContentTabLabel extends ContentTabLabel { private final TabbedContent myContent; private Reference myPopupReference = null; - public TabbedContentTabLabel(TabbedContent content, TabContentLayout layout) { + public TabbedContentTabLabel(@NotNull TabbedContent content, @NotNull TabContentLayout layout) { super(content, layout); myContent = content; - new ClickListener() { - @Override - public boolean onClick(@NotNull MouseEvent event, int clickCount) { - showPopup(); - return true; - } - }.installOn(this); } - private void showPopup() { + @Override + protected void selectContent() { IdeEventQueue.getInstance().getPopupManager().closeAllPopups(); - ArrayList names = new ArrayList(); - for (Pair tab : myContent.getTabs()) { - names.add(tab.first); + + if (!hasMultipleTabs()) { + super.selectContent(); + return; } - final JBList list = new JBList(names); - list.installCellRenderer(new NotNullFunction() { - private final JLabel label = new JLabel(); - { - label.setBorder(new EmptyBorder(UIUtil.getListCellPadding())); - } - @NotNull - @Override - public JComponent fun(Object dom) { - label.setText(dom.toString()); - return label; - } - }); - final JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list) - .setItemChoosenCallback(() -> { - int index = list.getSelectedIndex(); - if (index != -1) { - myContent.selectContent(index); - } - }).createPopup(); - myPopupReference = new WeakReference(popup); + + final SelectContentTabStep step = new SelectContentTabStep(getContent()); + final ListPopup popup = JBPopupFactory.getInstance().createListPopup(step); + myPopupReference = new WeakReference<>(popup); popup.showUnderneathOf(this); } @@ -102,19 +72,23 @@ public class TabbedContentTabLabel extends ContentTabLabel { if (myContent != null) { setText(myContent.getTabName()); } - setHorizontalAlignment(LEFT); + if (hasMultipleTabs()) { + setHorizontalAlignment(LEFT); + } } @Override public Dimension getPreferredSize() { final Dimension size = super.getPreferredSize(); - return new Dimension(size.width + 12, size.height); + return hasMultipleTabs() ? new Dimension(size.width + 12, size.height) : size; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); - myComboIcon.paintIcon(this, g); + if (hasMultipleTabs()) { + myComboIcon.paintIcon(this, g); + } } @Override @@ -126,4 +100,14 @@ public class TabbedContentTabLabel extends ContentTabLabel { myPopupReference = null; } } + + @NotNull + @Override + public TabbedContent getContent() { + return myContent; + } + + private boolean hasMultipleTabs() { + return myContent != null && myContent.hasMultipleTabs(); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java index ea0a2be5580b..992bbb9c64c8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -15,7 +15,6 @@ */ package com.intellij.openapi.wm.impl.content; -import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; import com.intellij.ide.actions.CloseAction; import com.intellij.ide.actions.ShowContentAction; @@ -28,7 +27,6 @@ import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.ToolWindowContentUiType; import com.intellij.openapi.wm.impl.ToolWindowImpl; @@ -76,7 +74,6 @@ public class ToolWindowContentUi extends JPanel implements ContentUI, PropertyCh ContentLayout myComboLayout = new ComboContentLayout(this); private ToolWindowContentUiType myType = ToolWindowContentUiType.TABBED; - private boolean myShouldNotShowPopup; public ToolWindowContentUi(ToolWindowImpl window) { myWindow = window; @@ -354,7 +351,7 @@ public class ToolWindowContentUi extends JPanel implements ContentUI, PropertyCh group.add(myPreviousTabAction); group.add(myShowContent); - if (content instanceof TabbedContent && ((TabbedContent)content).getTabs().size() > 1) { + if (content instanceof TabbedContent && ((TabbedContent)content).hasMultipleTabs()) { group.addAction(createSplitTabsAction((TabbedContent)content)); } @@ -509,69 +506,17 @@ public class ToolWindowContentUi extends JPanel implements ContentUI, PropertyCh } public void toggleContentPopup() { - if (myShouldNotShowPopup) { - myShouldNotShowPopup = false; - return; - } - final Ref selected = Ref.create(); - final Ref selectedTab = Ref.create(); final Content[] contents = myManager.getContents(); final Content selectedContent = myManager.getSelectedContent(); - final AnAction[] actions = new AnAction[contents.length]; - for (int i = 0; i < actions.length; i++) { - final Content content = contents[i]; - if (content instanceof TabbedContent) { - final TabbedContent tabbedContent = (TabbedContent)content; - final List> tabs = ((TabbedContent)content).getTabs(); - final AnAction[] tabActions = new AnAction[tabs.size()]; - for (int j = 0; j < tabActions.length; j++) { - final int index = j; - tabActions[j] = new DumbAwareAction(tabs.get(index).first) { - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - myManager.setSelectedContent(tabbedContent); - tabbedContent.selectContent(index); - } - }; - } - final DefaultActionGroup group = new DefaultActionGroup(tabActions); - group.getTemplatePresentation().setText(((TabbedContent)content).getTitlePrefix()); - group.setPopup(true); - actions[i] = group; - if (content == selectedContent) { - selected.set(group); - final int selectedIndex = ContentUtilEx.getSelectedTab(tabbedContent); - if (selectedIndex != -1) { - selectedTab.set(tabActions[selectedIndex]); - } - } - } else { - actions[i] = new DumbAwareAction() { - { - getTemplatePresentation().setText(content.getTabName(), false); - } - - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - myManager.setSelectedContent(content, true, true); - } - }; - if (content == selectedContent) { - selected.set(actions[i]); - } - } - } - - final ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(null, new DefaultActionGroup(actions), - DataManager.getInstance() - .getDataContext(myManager.getComponent()), false, true, - true, null, -1, action -> action == selected.get() || action == selectedTab.get()); + final SelectContentStep step = new SelectContentStep(contents); + step.setDefaultOptionIndex(myManager.getIndexOfContent(selectedContent)); + final ListPopup popup = JBPopupFactory.getInstance().createListPopup(step); getCurrentLayout().showContentPopup(popup); if (selectedContent instanceof TabbedContent) { - new Alarm(Alarm.ThreadToUse.SWING_THREAD, popup).addRequest(() -> popup.handleSelect(true), 30); + new Alarm(Alarm.ThreadToUse.SWING_THREAD, popup).addRequest(() -> popup.handleSelect(false), 50); } } } diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonLayoutImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonLayoutImpl.java index 694b7af17f31..55186d044540 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonLayoutImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonLayoutImpl.java @@ -134,22 +134,8 @@ public class BalloonLayoutImpl implements BalloonLayout { ApplicationManager.getApplication().assertIsDispatchThread(); Balloon merge = merge(layoutData); if (merge == null) { - if (getVisibleCount() > 0 && layoutData instanceof BalloonLayoutData && ((BalloonLayoutData)layoutData).groupId != null) { - int index = -1; - int count = 0; - for (int i = 0, size = myBalloons.size(); i < size; i++) { - BalloonLayoutData ld = myLayoutData.get(myBalloons.get(i)); - if (ld != null && ld.groupId != null) { - if (index == -1) { - index = i; - } - count++; - } - } - - if (count > 0 && count == getVisibleCount()) { - remove(myBalloons.get(index)); - } + if (!myBalloons.isEmpty() && myBalloons.size() == getVisibleCount()) { + remove(myBalloons.get(0)); } myBalloons.add(balloon); } diff --git a/platform/platform-impl/src/com/intellij/ui/FontComboBox.java b/platform/platform-impl/src/com/intellij/ui/FontComboBox.java index 93c4d48fd0ba..9d82923d1f55 100644 --- a/platform/platform-impl/src/com/intellij/ui/FontComboBox.java +++ b/platform/platform-impl/src/com/intellij/ui/FontComboBox.java @@ -16,9 +16,11 @@ package com.intellij.ui; import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.ui.ComboBox; import com.intellij.util.ui.FontInfo; +import org.jetbrains.annotations.Nullable; import java.awt.Dimension; import java.util.ArrayList; @@ -40,11 +42,11 @@ public final class FontComboBox extends ComboBox { } public FontComboBox(boolean withAllStyles) { - this(withAllStyles, true); + this(withAllStyles, true, false); } - public FontComboBox(boolean withAllStyles, boolean filterNonLatin) { - super(new Model(withAllStyles, filterNonLatin)); + public FontComboBox(boolean withAllStyles, boolean filterNonLatin, boolean noFontItem) { + super(new Model(withAllStyles, filterNonLatin, noFontItem)); Dimension size = getPreferredSize(); size.width = size.height * 8; setPreferredSize(size); @@ -68,10 +70,14 @@ public final class FontComboBox extends ComboBox { return item == null ? null : item.toString(); } - public void setFontName(String item) { + public void setFontName(@Nullable String item) { myModel.setSelectedItem(item); } + public boolean isNoFontSelected() { + return myModel.isNoFontSelected(); + } + @Override public void setModel(ComboBoxModel model) { if (model instanceof Model) { @@ -84,12 +90,14 @@ public final class FontComboBox extends ComboBox { } private static final class Model extends AbstractListModel implements ComboBoxModel { + private final NoFontItem NO_FONT_ITEM; private volatile List myAllFonts = Collections.emptyList(); private volatile List myMonoFonts = Collections.emptyList(); private boolean myMonospacedOnly; private Object mySelectedItem; - private Model(boolean withAllStyles, boolean filterNonLatin) { + private Model(boolean withAllStyles, boolean filterNonLatin, boolean noFontItem) { + NO_FONT_ITEM = noFontItem ? new NoFontItem() : null; Application application = ApplicationManager.getApplication(); if (application == null || application.isUnitTestMode()) { setFonts(FontInfo.getAll(withAllStyles), filterNonLatin); @@ -132,17 +140,22 @@ public final class FontComboBox extends ComboBox { } @Override - public void setSelectedItem(Object item) { - if (item instanceof FontInfo) { - FontInfo info = getInfo(item); - if (info == null) { - List list = myMonospacedOnly ? myMonoFonts : myAllFonts; - item = list.isEmpty() ? null : list.get(0); - } + public void setSelectedItem(@Nullable Object item) { + if (item == null && NO_FONT_ITEM != null) { + item = NO_FONT_ITEM; } - if (item instanceof String) { - FontInfo info = getInfo(item); - if (info != null) item = info; + else { + if (item instanceof FontInfo) { + FontInfo info = getInfo(item); + if (info == null) { + List list = myMonospacedOnly ? myMonoFonts : myAllFonts; + item = list.isEmpty() ? null : list.get(0); + } + } + if (item instanceof String) { + FontInfo info = getInfo(item); + if (info != null) item = info; + } } if (!(mySelectedItem == null ? item == null : mySelectedItem.equals(item))) { mySelectedItem = item; @@ -150,16 +163,28 @@ public final class FontComboBox extends ComboBox { } } + public boolean isNoFontSelected() { + return getSelectedItem() == NO_FONT_ITEM; + } + @Override public int getSize() { List list = myMonospacedOnly ? myMonoFonts : myAllFonts; - return mySelectedItem instanceof String ? 1 + list.size() : list.size(); + int size = list.size(); + if (mySelectedItem instanceof String) size ++; + if (NO_FONT_ITEM != null) size++; + return size; } @Override public Object getElementAt(int index) { + int i = index; + if (NO_FONT_ITEM != null) { + if (index == 0) return NO_FONT_ITEM; + i --; + } List list = myMonospacedOnly ? myMonoFonts : myAllFonts; - return 0 <= index && index < list.size() ? list.get(index) : mySelectedItem; + return 0 <= i && i < list.size() ? list.get(i) : mySelectedItem; } private FontInfo getInfo(Object item) { @@ -170,5 +195,12 @@ public final class FontComboBox extends ComboBox { } return null; } + + private final static class NoFontItem { + @Override + public String toString() { + return ApplicationBundle.message("settings.editor.font.none"); + } + } } } diff --git a/platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java b/platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java index cd52c0383806..8e8910480bd6 100644 --- a/platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -95,8 +95,8 @@ public class TabbedContentImpl extends ContentImpl implements TabbedContent { setDisplayName(tab.first); setComponent(tab.second); } - - public int getSelectedContent() { + + public int getSelectedIndex() { JComponent selected = getComponent(); for (int i = 0; i < myTabs.size(); i++) { if (myTabs.get(i).second == selected) return i; diff --git a/platform/platform-impl/src/com/intellij/util/ContentUtilEx.java b/platform/platform-impl/src/com/intellij/util/ContentUtilEx.java index e699fbc8dd81..3eb528ad9524 100644 --- a/platform/platform-impl/src/com/intellij/util/ContentUtilEx.java +++ b/platform/platform-impl/src/com/intellij/util/ContentUtilEx.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -185,6 +185,9 @@ public class ContentUtilEx extends ContentsUtil { } public static int getSelectedTab(@NotNull TabbedContent content) { + int selectedIndex = content.getSelectedIndex(); + if (selectedIndex != -1) return selectedIndex; + final JComponent current = content.getComponent(); int index = 0; for (Pair tab : content.getTabs()) { diff --git a/platform/platform-impl/src/com/intellij/util/PsiErrorElementUtil.java b/platform/platform-impl/src/com/intellij/util/PsiErrorElementUtil.java index 5e9bc6559751..818c2fbfd728 100644 --- a/platform/platform-impl/src/com/intellij/util/PsiErrorElementUtil.java +++ b/platform/platform-impl/src/com/intellij/util/PsiErrorElementUtil.java @@ -20,11 +20,7 @@ import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiErrorElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.SyntaxTraverser; -import com.intellij.psi.impl.PsiManagerEx; +import com.intellij.psi.*; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; @@ -39,9 +35,7 @@ public class PsiErrorElementUtil { public static boolean hasErrors(@NotNull Project project, @NotNull VirtualFile virtualFile) { return ReadAction.compute(() -> { if (project.isDisposed() || !virtualFile.isValid()) return false; - - PsiManagerEx psiManager = PsiManagerEx.getInstanceEx(project); - PsiFile psiFile = psiManager.getFileManager().findFile(virtualFile); + PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile); return psiFile != null && hasErrors(psiFile); }); } diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 9668a52b2261..3b9f5d210601 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -542,10 +542,11 @@ error.a.scheme.with.this.name.already.exists.or.was.deleted.without.applying.the title.select.font=Select Font action.apply.editor.font.settings=Apply editor font settings checkbox.show.only.monospaced.fonts=Show only monospaced fonts -primary.font=Primary font: -secondary.font=Secondary font: +primary.font=Font: +secondary.font=Fallback font: use.ligatures=Enable font ligatures ligatures.jre.warning=The JRE you are running {0} with
is known to have performance issues
related to ligatures support.
Use JetBrains Runtime instead to enable ligatures. +settings.editor.font.none= editbox.enter.tag.name=Enter tag name: title.tag.name=Tag Name title.xml=XML @@ -574,7 +575,7 @@ title.scope.based=By Scope title.colors.and.fonts=Colors \\& Fonts progress.analysing.font=Analysing font: {0} group.editor.font=Editor Font -label.fallback.fonts.list.description=If primary font fails, IDE tries to use the secondary one +label.fallback.fonts.list.description=For symbols not supported by the main font quickdoc.tooltip.font.size.by.wheel=Slider or Ctrl+Wheel change font size label.font.size=Font size: label.font.name=Name: diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index 0a383fea75a1..cb4f0a6010ae 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -195,6 +195,7 @@ + @@ -375,6 +376,7 @@ + diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java index dd3fd78053ae..cda617917d11 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java @@ -117,6 +117,7 @@ import com.intellij.testFramework.*; import com.intellij.testFramework.fixtures.*; import com.intellij.testFramework.utils.inlays.InlayHintsChecker; import com.intellij.ui.breadcrumbs.BreadcrumbsProvider; +import com.intellij.ui.breadcrumbs.BreadcrumbsUtil; import com.intellij.ui.components.breadcrumbs.Crumb; import com.intellij.usageView.UsageInfo; import com.intellij.util.*; @@ -1791,8 +1792,8 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig } final Language language = element.getContainingFile().getLanguage(); - final BreadcrumbsProvider provider = ContainerUtil.find(BreadcrumbsProvider.EP_NAME.getExtensions(), - p -> Arrays.asList(p.getLanguages()).contains(language)); + final BreadcrumbsProvider provider = BreadcrumbsUtil.getInfoProvider(language); + if (provider == null) { return Collections.emptyList(); } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 429de9f36601..1e07581f188c 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -277,6 +277,7 @@ debugger.capture.points.annotations=false debugger.resume.yourkit.threads=false debugger.keep.step.requests=false debugger.enable.memory.view=true +debugger.tree.states.depth=100 analyze.exceptions.on.the.fly=false analyze.exceptions.on.the.fly.description=Automatically analyze clipboard on frame activation,\ diff --git a/platform/util/src/com/intellij/icons/AllIcons.java b/platform/util/src/com/intellij/icons/AllIcons.java index 6a4a440e3110..ed75689aa016 100644 --- a/platform/util/src/com/intellij/icons/AllIcons.java +++ b/platform/util/src/com/intellij/icons/AllIcons.java @@ -1289,6 +1289,7 @@ public class AllIcons { public static final Icon Push = IconLoader.getIcon("/vcs/push.png"); // 16x16 public static final Icon Remove = IconLoader.getIcon("/vcs/remove.png"); // 16x16 public static final Icon ResetStrip = IconLoader.getIcon("/vcs/resetStrip.png"); // 16x16 + public static final Icon RestoreDefaultSize = IconLoader.getIcon("/vcs/restoreDefaultSize.png"); // 16x16 public static final Icon Shelve = IconLoader.getIcon("/vcs/Shelve.png"); // 16x16 public static final Icon ShelveSilent = IconLoader.getIcon("/vcs/shelveSilent.png"); // 16x16 public static final Icon ShowUnversionedFiles = IconLoader.getIcon("/vcs/ShowUnversionedFiles.png"); // 16x16 diff --git a/platform/util/src/com/intellij/util/text/SemVer.java b/platform/util/src/com/intellij/util/text/SemVer.java index 664aa224ab7d..347a1b07b11a 100644 --- a/platform/util/src/com/intellij/util/text/SemVer.java +++ b/platform/util/src/com/intellij/util/text/SemVer.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.Nullable; * Holds Semantic Version. */ public final class SemVer implements Comparable { + /** @deprecated */ public static final SemVer UNKNOWN = new SemVer("?", 0, 0, 0); private final String myRawVersion; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java index a43c2189bb97..103acc7c0821 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java @@ -73,7 +73,6 @@ import java.awt.event.KeyEvent; import java.util.Collection; import java.util.List; -import static com.intellij.util.ObjectUtils.assertNotNull; import static java.util.stream.Collectors.toList; @State( @@ -83,7 +82,7 @@ import static java.util.stream.Collectors.toList; public class ChangesViewManager implements ChangesViewI, ProjectComponent, PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.ChangesViewManager"); - public static final String CHANGES_VIEW_PREVIEW_SPLITTER_PROPORTION = "ChangesViewManager.DETAILS_SPLITTER_PROPORTION"; + private static final String CHANGES_VIEW_PREVIEW_SPLITTER_PROPORTION = "ChangesViewManager.DETAILS_SPLITTER_PROPORTION"; @NotNull private final ChangesListView myView; private JPanel myProgressLabel; @@ -478,10 +477,14 @@ public class ChangesViewManager implements ChangesViewI, ProjectComponent, Persi private class ToggleDetailsAction extends ShowDiffPreviewAction { @Override public void setSelected(AnActionEvent e, boolean state) { - super.setSelected(e, state); - assertNotNull(mySplitterComponent).setDetailsOn(state); + mySplitterComponent.setDetailsOn(state); VcsConfiguration.getInstance(myProject).LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN = state; } + + @Override + public boolean isSelected(AnActionEvent e) { + return VcsConfiguration.getInstance(myProject).LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN; + } } private class MyChangeProcessor extends ChangeViewDiffRequestProcessor { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java index 068a97b8ea16..56528d7d599b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java @@ -16,24 +16,11 @@ package com.intellij.openapi.vcs.changes.actions; import com.intellij.icons.AllIcons; -import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.DumbAware; public abstract class ShowDiffPreviewAction extends ToggleAction implements DumbAware { - private boolean myState; - public ShowDiffPreviewAction() { super("Preview Diff", null, AllIcons.Actions.DiffPreview); } - - @Override - public boolean isSelected(AnActionEvent e) { - return myState; - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - myState = state; - } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java index bad6e597a4d3..772fc2514639 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java @@ -245,7 +245,7 @@ public class DiffShelvedChangesActionProvider implements AnActionExtensionProvid DiffContentFactory contentFactory = DiffContentFactory.getInstance(); DiffContent leftContent = withLocal ? contentFactory.create(project, file) - : contentFactory.create(project, patch.getSingleHunkPatchText()); + : contentFactory.create(project, patch.getSingleHunkPatchText(), file); return new SimpleDiffRequest(getName(), leftContent, contentFactory.createEmpty(), withLocal ? CURRENT_VERSION : SHELVED_VERSION, null); @@ -269,8 +269,8 @@ public class DiffShelvedChangesActionProvider implements AnActionExtensionProvid DiffContentFactory contentFactory = DiffContentFactory.getInstance(); DiffContent leftContent = withLocal ? contentFactory.create(project, file) - : contentFactory.create(project, assertNotNull(texts.getBase())); - return new SimpleDiffRequest(getName(), leftContent, contentFactory.create(project, texts.getPatched()), + : contentFactory.create(project, assertNotNull(texts.getBase()), file); + return new SimpleDiffRequest(getName(), leftContent, contentFactory.create(project, texts.getPatched(), file), withLocal ? CURRENT_VERSION : BASE_VERSION, SHELVED_VERSION); } else { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java index a811a1a7c1ab..76c6bc03fd89 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java @@ -108,6 +108,7 @@ public class ShelvedChangesViewManager implements ProjectComponent { private static final Logger LOG = Logger.getInstance(ShelvedChangesViewManager.class); @NonNls static final String SHELF_CONTEXT_MENU = "Vcs.Shelf.ContextMenu"; + private static final String SHELVE_PREVIEW_SPLITTER_PROPORTION = "ShelvedChangesViewManager.DETAILS_SPLITTER_PROPORTION"; private final ChangesViewContentManager myContentManager; private final ShelveChangesManager myShelveChangesManager; @@ -247,21 +248,11 @@ public class ShelvedChangesViewManager implements ProjectComponent { DefaultActionGroup actionGroup = new DefaultActionGroup(); actionGroup.addAll((ActionGroup)ActionManager.getInstance().getAction("ShelvedChangesToolbar")); - ShowDiffPreviewAction diffPreviewAction = new ShowDiffPreviewAction() { - @Override - public void setSelected(AnActionEvent e, boolean state) { - super.setSelected(e, state); - assertNotNull(mySplitterComponent).setDetailsOn(state); - VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN = state; - } - }; - actionGroup.add(diffPreviewAction, new Constraints(AFTER, "ShelvedChanges.ShowHideDeleted")); + actionGroup.add(new MyToggleDetailsAction(), new Constraints(AFTER, "ShelvedChanges.ShowHideDeleted")); MyShelvedPreviewProcessor changeProcessor = new MyShelvedPreviewProcessor(myProject); - mySplitterComponent = - new PreviewDiffSplitterComponent(pane, changeProcessor, "ShelvedChangesViewManager.DETAILS_SPLITTER_PROPORTION", - VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN); - diffPreviewAction.setSelected(null, mySplitterComponent.isDetailsOn()); + mySplitterComponent = new PreviewDiffSplitterComponent(pane, changeProcessor, SHELVE_PREVIEW_SPLITTER_PROPORTION, + VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN); ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("ShelvedChanges", actionGroup, false); JPanel rootPanel = new JPanel(new BorderLayout()); @@ -742,6 +733,19 @@ public class ShelvedChangesViewManager implements ProjectComponent { return new DnDImage(image, new Point(-image.getWidth(null), -image.getHeight(null))); } + private class MyToggleDetailsAction extends ShowDiffPreviewAction { + @Override + public void setSelected(AnActionEvent e, boolean state) { + mySplitterComponent.setDetailsOn(state); + VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN = state; + } + + @Override + public boolean isSelected(AnActionEvent e) { + return VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN; + } + } + private class MyShelvedPreviewProcessor extends CacheDiffRequestProcessor implements DiffPreviewUpdateProcessor { @NotNull private final DiffShelvedChangesActionProvider.PatchesPreloader myPreloader; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java index 61470653fda3..0cf752a66693 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogFullDetailsIndex.java @@ -31,7 +31,6 @@ import com.intellij.vcs.log.util.PersistentUtil; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; -import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Map; @@ -130,11 +129,6 @@ public class VcsLogFullDetailsIndex implements Disposable { if (myDisposed) throw new ProcessCanceledException(); } - @NotNull - public static File getStorageFile(@NotNull String kind, @NotNull String id) { - return PersistentUtil.getStorageFile(INDEX, kind, id, getVersion(), false); - } - private class MyMapReduceIndex extends MapReduceIndex { public MyMapReduceIndex(@NotNull DataIndexer indexer, @NotNull DataExternalizer externalizer, @@ -157,7 +151,7 @@ public class VcsLogFullDetailsIndex implements Disposable { private static class MyMapIndexStorage extends MapIndexStorage { public MyMapIndexStorage(@NotNull String name, @NotNull String logId, @NotNull DataExternalizer externalizer) throws IOException { - super(VcsLogFullDetailsIndex.getStorageFile(name, logId), EnumeratorIntegerDescriptor.INSTANCE, externalizer, 5000, false); + super(PersistentUtil.getStorageFile(INDEX, name, logId, getVersion()), EnumeratorIntegerDescriptor.INSTANCE, externalizer, 5000, false); } @Override diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java index 899713a03e5b..9347f8fa478b 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java @@ -71,7 +71,7 @@ public class VcsLogPathsIndex extends VcsLogFullDetailsIndex createPathsEnumerator(@NotNull String logId) throws IOException { - File storageFile = PersistentUtil.getStorageFile(INDEX, INDEX_PATHS_IDS, logId, getVersion(), true); + File storageFile = PersistentUtil.getStorageFile(INDEX, INDEX_PATHS_IDS, logId, getVersion()); return new PersistentBTreeEnumerator<>(storageFile, SystemInfo.isFileSystemCaseSensitive ? EnumeratorStringDescriptor.INSTANCE : new ToLowerCaseStringDescriptor(), Page.PAGE_SIZE, null, getVersion()); @@ -309,7 +309,8 @@ public class VcsLogPathsIndex extends VcsLogFullDetailsIndex beforeData = fillDataWithNulls(result, parent, beforeId); beforeData.add(new ChangeData(ChangeKind.RENAMED_FROM, afterId)); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java index f9a4eeeb5874..d74819c3b7e1 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java @@ -415,11 +415,11 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { try { int version = getVersion(); - File commitsStorage = getStorageFile(INDEX, COMMITS, logId, version, true); + File commitsStorage = getStorageFile(INDEX, COMMITS, logId, version); commits = new PersistentSetImpl<>(commitsStorage, EnumeratorIntegerDescriptor.INSTANCE, Page.PAGE_SIZE, null, version); Disposer.register(disposable, () -> catchAndWarn(commits::close)); - File messagesStorage = getStorageFile(INDEX, MESSAGES, logId, VcsLogStorageImpl.VERSION + MESSAGES_VERSION, true); + File messagesStorage = getStorageFile(INDEX, MESSAGES, logId, VcsLogStorageImpl.VERSION + MESSAGES_VERSION); messages = new PersistentHashMap<>(messagesStorage, new IntInlineKeyDescriptor(), EnumeratorStringDescriptor.INSTANCE, Page.PAGE_SIZE); Disposer.register(disposable, () -> catchAndWarn(messages::close)); @@ -428,9 +428,9 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { users = new VcsLogUserIndex(logId, userRegistry, fatalErrorHandler, disposable); paths = new VcsLogPathsIndex(logId, roots, fatalErrorHandler, disposable); - File parentsStorage = getStorageFile(INDEX, PARENTS, logId, getVersion(), true); + File parentsStorage = getStorageFile(INDEX, PARENTS, logId, version); parents = new PersistentHashMap<>(parentsStorage, EnumeratorIntegerDescriptor.INSTANCE, - new IntListDataExternalizer(), Page.PAGE_SIZE, getVersion()); + new IntListDataExternalizer(), Page.PAGE_SIZE, version); Disposer.register(disposable, () -> catchAndWarn(parents::close)); } catch (Throwable t) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java index e81891dd7d9f..8f6da02b8bf7 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java @@ -110,17 +110,23 @@ public class PersistentUtil { public static File getStorageFile(@NotNull String subdirName, @NotNull String kind, @NotNull String id, - int version, - boolean cleanupOldVersions) { + int version) { File subdir = new File(LOG_CACHE, subdirName); String safeLogId = PathUtilRt.suggestFileName(id, true, true); - File file = new File(subdir, safeLogId + "." + kind + "." + version); - if (cleanupOldVersions && !file.exists()) { - IOUtil.deleteAllFilesStartingWith(new File(subdir, safeLogId + "." + kind)); + File file = getFileName(kind, subdir, safeLogId, version); + if (!file.exists()) { + for (int oldVersion = 0; oldVersion < version; oldVersion++) { + IOUtil.deleteAllFilesStartingWith(getFileName(kind, subdir, safeLogId, oldVersion)); + } } return file; } + @NotNull + private static File getFileName(@NotNull String kind, @NotNull File subdir, @NotNull String safeLogId, int version) { + return new File(subdir, safeLogId + "." + kind + "." + version); + } + @NotNull public static File getCorruptionMarkerFile() { return new File(LOG_CACHE, CORRUPTION_MARKER); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java index 2474d73f95b3..205f176968c0 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java @@ -51,18 +51,24 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.util.LinkedHashMap; +import java.util.Map; /** * @author nik */ public abstract class XVariablesViewBase extends XDebugView { private final XDebuggerTreePanel myTreePanel; - private XDebuggerTreeState myTreeState; - private XDebuggerTreeRestorer myTreeRestorer; - - private Object myFrameEqualityObject; private MySelectionListener mySelectionListener; + private XDebuggerTreeRestorer myTreeRestorer; + private final Map myTreeStates = new LinkedHashMap() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > Registry.get("debugger.tree.states.depth").asInteger(); + } + }; + protected XVariablesViewBase(@NotNull Project project, @NotNull XDebuggerEditorsProvider editorsProvider, @Nullable XValueMarkers markers) { myTreePanel = new XDebuggerTreePanel( project, editorsProvider, this, null, this instanceof XWatchesView ? XDebuggerActions.WATCHES_TREE_POPUP_GROUP : XDebuggerActions.VARIABLES_TREE_POPUP_GROUP, markers); @@ -79,11 +85,14 @@ public abstract class XVariablesViewBase extends XDebugView { project.putUserData(XVariablesView.DEBUG_VARIABLES, new XVariablesView.InlineVariablesInfo()); clearInlays(tree); Object newEqualityObject = stackFrame.getEqualityObject(); - if (myFrameEqualityObject != null && newEqualityObject != null && myFrameEqualityObject.equals(newEqualityObject) - && myTreeState != null) { - disposeTreeRestorer(); - myTreeRestorer = myTreeState.restoreState(tree); + if (newEqualityObject != null) { + XDebuggerTreeState state = myTreeStates.get(newEqualityObject); + if (state != null) { + disposeTreeRestorer(); + myTreeRestorer = state.restoreState(tree); + } } + if (position != null && Registry.is("debugger.valueTooltipAutoShowOnSelection")) { registerInlineEvaluator(stackFrame, position, project); } @@ -120,9 +129,9 @@ public abstract class XVariablesViewBase extends XDebugView { protected void saveCurrentTreeState(@Nullable XStackFrame stackFrame) { removeSelectionListener(); - myFrameEqualityObject = stackFrame != null ? stackFrame.getEqualityObject() : null; - if (myTreeRestorer == null || myTreeRestorer.isFinished()) { - myTreeState = XDebuggerTreeState.saveState(getTree()); + Object equalityObject = stackFrame != null ? stackFrame.getEqualityObject() : null; + if (equalityObject != null && (myTreeRestorer == null || myTreeRestorer.isFinished())) { + myTreeStates.put(equalityObject, XDebuggerTreeState.saveState(getTree())); } disposeTreeRestorer(); } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index 3828297a94dd..00491cad2657 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -45,8 +45,8 @@ equals.called.on.array.display.name='equals()' called on array equals.called.on.array.problem.descriptor=#ref() between arrays should probably be 'Arrays.equals()' #loc replace.with.arrays.equals=Replace with 'Arrays.equals()' replace.with.arrays.deep.equals=Replace with 'Arrays.deepEquals()' -assignment.to.null.display.name=Assignment to 'null' -assignment.to.null.problem.descriptor=Assignment of variable #ref to 'null' #loc +assignment.to.null.display.name='null' assignment +assignment.to.null.problem.descriptor='null' assigned to variable #ref #loc assignment.to.null.option=Ignore assignments to fields assignment.to.static.field.from.instance.method.display.name=Assignment to static field from instance context assignment.to.static.field.from.instance.method.problem.descriptor=Assignment to static field #ref from instance context #loc @@ -1448,7 +1448,7 @@ integer.multiplication.implicit.cast.to.long.option=Ignore compile time co wait.or.await.without.timeout.display.name='wait()' or 'await()' without timeout wait.or.await.without.timeout.problem.descriptor=#ref without timeout #loc method.return.always.constant.display.name=Method returns per-class constant -method.return.always.constant.problem.descriptor=Method #ref() returns a per-class constant +method.return.always.constant.problem.descriptor=Method #ref() and all it's derivables always return constants class.with.too.many.dependencies.display.name=Class with too many dependencies class.with.too.many.dependencies.problem.descriptor=Class ''{0}'' has too many dependencies ({1} > {2}) class.with.too.many.transitive.dependencies.display.name=Class with too many transitive dependencies diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/FormatDecode.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/FormatDecode.java index 28cdf3b653c3..3de143f98144 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/FormatDecode.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/FormatDecode.java @@ -248,7 +248,7 @@ class FormatDecode { } storeValidator(allowed, pos, parameters, argumentCount); } - if (i < formatString.length() - 1) { + if (i < formatString.length()) { checkText(formatString.substring(i)); } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java index fbced3b0f8c0..b475ee5d422f 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java @@ -17,9 +17,7 @@ package com.siyeh.ig.classlayout; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInspection.*; -import com.intellij.codeInspection.reference.RefEntity; -import com.intellij.codeInspection.reference.RefMethod; -import com.intellij.openapi.util.Key; +import com.intellij.codeInspection.reference.*; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; import com.siyeh.InspectionGadgetsBundle; @@ -34,14 +32,10 @@ import java.util.Set; public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { - private static final Key ALWAYS_CONSTANT = - Key.create("ALWAYS_CONSTANT"); - @NotNull @Override public String getDisplayName() { - return InspectionGadgetsBundle.message( - "method.return.always.constant.display.name"); + return InspectionGadgetsBundle.message("method.return.always.constant.display.name"); } @Override @@ -51,35 +45,24 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { if (!(refEntity instanceof RefMethod)) { return null; } - final RefMethod refMethod = (RefMethod)refEntity; - final Boolean alreadyProcessed = refMethod.getUserData(ALWAYS_CONSTANT); - if (alreadyProcessed != null && alreadyProcessed.booleanValue()) { + //don't warn on overriders + if (((RefMethod)refEntity).hasSuperMethods()) { return null; } + final RefMethod refMethod = (RefMethod)refEntity; if (!(refMethod.getElement() instanceof PsiMethod)) { return null; } - final PsiMethod method = (PsiMethod)refMethod.getElement(); - if (method.getBody() == null) { - return null; //we'll catch it on another method - } - if (!alwaysReturnsConstant(method)) { - return null; - } - final Set siblingMethods = - MethodInheritanceUtils.calculateSiblingMethods(refMethod); - for (RefMethod siblingMethod : siblingMethods) { - final PsiMethod siblingPsiMethod = - (PsiMethod)siblingMethod.getElement(); - if (method.getBody() != null && - !alwaysReturnsConstant(siblingPsiMethod)) { + final Set allScopeInheritors = MethodInheritanceUtils.calculateSiblingMethods(refMethod); + for (RefMethod siblingMethod : allScopeInheritors) { + final PsiMethod siblingPsiMethod = (PsiMethod)siblingMethod.getElement(); + if (siblingPsiMethod.getBody() != null && !alwaysReturnsConstant(siblingPsiMethod)) { return null; } } final List out = new ArrayList<>(); - for (RefMethod siblingRefMethod : siblingMethods) { - final PsiMethod siblingMethod = - (PsiMethod)siblingRefMethod.getElement(); + for (RefMethod siblingRefMethod : allScopeInheritors) { + final PsiMethod siblingMethod = (PsiMethod)siblingRefMethod.getElement(); final PsiIdentifier identifier = siblingMethod.getNameIdentifier(); if (identifier == null) { continue; @@ -88,8 +71,6 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { InspectionGadgetsBundle.message( "method.return.always.constant.problem.descriptor"), false, null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)); - siblingRefMethod.putUserData(ALWAYS_CONSTANT, - Boolean.valueOf(true)); } return out.toArray(new ProblemDescriptor[out.size()]); } @@ -100,9 +81,32 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { if (!(statement instanceof PsiReturnStatement)) { return false; } - final PsiReturnStatement returnStatement = - (PsiReturnStatement)statement; + final PsiReturnStatement returnStatement = (PsiReturnStatement)statement; final PsiExpression value = returnStatement.getReturnValue(); return value != null && PsiUtil.isConstantExpression(value); } + + @Override + protected boolean queryExternalUsagesRequests(@NotNull final RefManager manager, @NotNull final GlobalJavaInspectionContext globalContext, + @NotNull final ProblemDescriptionsProcessor processor) { + manager.iterate(new RefJavaVisitor() { + @Override public void visitElement(@NotNull RefEntity refEntity) { + if (refEntity instanceof RefElement && processor.getDescriptions(refEntity) != null) { + refEntity.accept(new RefJavaVisitor() { + @Override public void visitMethod(@NotNull final RefMethod refMethod) { + globalContext.enqueueDerivedMethodsProcessor(refMethod, new GlobalJavaInspectionContext.DerivedMethodsProcessor() { + @Override + public boolean process(PsiMethod derivedMethod) { + processor.ignoreElement(refMethod); + return false; + } + }); + } + }); + } + } + }); + + return false; + } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/methodmetrics/ParametersPerMethodInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/methodmetrics/ParametersPerMethodInspection.java index b760b1ec3936..ea717056a154 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/methodmetrics/ParametersPerMethodInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/methodmetrics/ParametersPerMethodInspection.java @@ -19,7 +19,6 @@ import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiParameterList; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.LibraryUtil; import org.jetbrains.annotations.NotNull; public class ParametersPerMethodInspection extends MethodMetricInspection { @@ -76,7 +75,8 @@ public class ParametersPerMethodInspection extends MethodMetricInspection { if (parametersCount <= getLimit()) { return; } - if (LibraryUtil.isOverrideOfLibraryMethod(method)) { + //skip all derivatives + if (method.findDeepestSuperMethods().length > 0) { return; } registerMethodError(method, Integer.valueOf(parametersCount)); diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html index 05c7d7cbdbcc..a48b19716916 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html @@ -1,6 +1,6 @@ -Reports methods which only ever return a constant. +Reports methods which only ever return a constant, probably different for different inheritors. Because this inspection requires global code analysis it is only available for Analyze|Inspect Code or Analyze|Run Inspection by Name and it will not report in the editor. diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/assignment/assignment_to_null/AssignmentToNull.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/assignment/assignment_to_null/AssignmentToNull.java index 2a0d001d5bea..eb579752830e 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/assignment/assignment_to_null/AssignmentToNull.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/assignment/assignment_to_null/AssignmentToNull.java @@ -7,7 +7,7 @@ public class AssignmentToNull public static void main(String[] args) { new AssignmentToNull(new Object()).bar(); - args[0] = null; + args[0] = null; } public AssignmentToNull(Object foo) @@ -19,8 +19,8 @@ public class AssignmentToNull { Object foo = new Object(); System.out.println("foo = " + foo); - foo = null; - m_foo = null; + foo = null; + m_foo = null; System.out.println("foo = " + foo); System.out.println("m_foo = " + m_foo); } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java index ee50bbfe8df2..951cd1c10cdb 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java @@ -7,16 +7,15 @@ import java.util.Formattable; public class MalformedFormatString { - public void foo() - { - String.format("%", 3.0); - System.out.printf("%", 3.0); + public void foo() { + String.format("%%", 3.0); + System.out.printf("%s", 3.0, 2.0); System.out.printf("%q", 3.0); System.out.printf("%d", 3.0); System.out.printf(new Locale(""),"%d%s", 3.0, "foo"); } - public static void main(String[] args) { + public static void main(String[] args) { String local = "hmm"; String good = String.format("%s %s", 1, 2); // this is valid according to the inspector (correct) @@ -60,6 +59,7 @@ public class MalformedFormatString { void badStrings() { // bad format specifier String.format("%) %n"); + String.format("%d%", 1); // flags on newline not allowed String.format("%(n"); diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/assignment/AssignmentToNullInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/assignment/AssignmentToNullInspectionTest.java index c43e4501f47e..13e64e321b9e 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/assignment/AssignmentToNullInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/assignment/AssignmentToNullInspectionTest.java @@ -17,7 +17,6 @@ package com.siyeh.ig.assignment; import com.intellij.codeInspection.InspectionProfileEntry; import com.siyeh.ig.LightInspectionTestCase; -import junit.framework.TestCase; import org.jetbrains.annotations.Nullable; /** diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java index 40649feaf573..9c2e48b07843 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java @@ -709,7 +709,7 @@ public class InjectionsSettingsUI extends SearchableConfigurable.Parent.Abstract } private void doImportAction(final DataContext dataContext) { - final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, true, false, true, false) { + final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, true, false) { @Override public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) { return super.isFileVisible(file, showHiddenFiles) && diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java index dc2a3880f9db..af23015cfa15 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2015 Bas Leijdekkers + * Copyright 2008-2017 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package com.siyeh.ipp.concatenation; import com.intellij.psi.*; import com.intellij.psi.util.PsiConcatenationUtil; -import com.intellij.util.IncorrectOperationException; import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ipp.base.Intention; @@ -36,7 +35,7 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention { } @Override - protected void processIntention(@NotNull PsiElement element) throws IncorrectOperationException { + protected void processIntention(@NotNull PsiElement element) { PsiPolyadicExpression expression = (PsiPolyadicExpression)element; PsiElement parent = expression.getParent(); while (ExpressionUtils.isConcatenation(parent)) { @@ -44,7 +43,7 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention { parent = expression.getParent(); } final StringBuilder formatString = new StringBuilder(); - final List formatParameters = new ArrayList(); + final List formatParameters = new ArrayList<>(); PsiConcatenationUtil.buildFormatString(expression, formatString, formatParameters, true); if (replaceWithPrintfExpression(expression, formatString, formatParameters)) { return; @@ -62,7 +61,7 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention { } private static boolean replaceWithPrintfExpression(PsiExpression expression, CharSequence formatString, - List formatParameters) throws IncorrectOperationException { + List formatParameters) { final PsiElement expressionParent = expression.getParent(); if (!(expressionParent instanceof PsiExpressionList)) { return false; @@ -100,18 +99,15 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention { final StringBuilder newExpression = new StringBuilder(); final PsiExpression qualifier = methodExpression.getQualifierExpression(); if (qualifier != null) { - newExpression.append(qualifier.getText()); - newExpression.append('.'); + newExpression.append(qualifier.getText()).append('.'); } - newExpression.append("printf(\""); - newExpression.append(formatString); + newExpression.append("printf(\"").append(formatString); if (insertNewline) { newExpression.append("%n"); } newExpression.append('\"'); for (PsiExpression formatParameter : formatParameters) { - newExpression.append(", "); - newExpression.append(formatParameter.getText()); + newExpression.append(", ").append(formatParameter.getText()); } newExpression.append(')'); PsiReplacementUtil.replaceExpression(methodCallExpression, newExpression.toString()); diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/SimpleStringConcatenationPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/SimpleStringConcatenationPredicate.java index 1b5e41ed0fb7..8393cbd955bf 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/SimpleStringConcatenationPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/SimpleStringConcatenationPredicate.java @@ -16,7 +16,9 @@ package com.siyeh.ipp.concatenation; import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.psi.PsiAnnotationMethod; import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ipp.base.PsiElementPredicate; @@ -33,6 +35,7 @@ class SimpleStringConcatenationPredicate implements PsiElementPredicate { if (!ExpressionUtils.isConcatenation(element)) { return false; } - return !(excludeConcatenationsInsideAnnotations && AnnotationUtil.isInsideAnnotation(element)); + return !(excludeConcatenationsInsideAnnotations && (AnnotationUtil.isInsideAnnotation(element) || + PsiTreeUtil.getParentOfType(element, PsiAnnotationMethod.class) != null)); } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/functional/ExtractToMethodReferenceIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/functional/ExtractToMethodReferenceIntention.java index 065bfd5899af..294f6f5074e9 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/functional/ExtractToMethodReferenceIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/functional/ExtractToMethodReferenceIntention.java @@ -130,7 +130,7 @@ public class ExtractToMethodReferenceIntention extends BaseElementAtCaretIntenti targetMethodName + parameters, targetClass); PsiCodeBlock targetMethodBody = emptyMethod.getBody(); LOG.assertTrue(targetMethodBody != null); - targetMethodBody.addRange(elements[0], elements[elements.length - 1]); + if (elements.length > 0) targetMethodBody.addRange(elements[0], elements[elements.length - 1]); PsiMethod method = (PsiMethod)CodeStyleManager.getInstance(project).reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(targetClass.add(emptyMethod))); PsiMethodReferenceExpression methodReference = diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java index aebb8f568814..c7f1a29a45aa 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java @@ -16,8 +16,11 @@ package com.siyeh.ipp.initialization; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.refactoring.util.RefactoringUtil; import com.siyeh.IntentionPowerPackBundle; import com.siyeh.ipp.base.Intention; @@ -26,6 +29,8 @@ import com.siyeh.ipp.psiutils.HighlightUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import java.util.function.Predicate; + public class SplitDeclarationAndInitializationIntention extends Intention { @Override @@ -46,58 +51,55 @@ public class SplitDeclarationAndInitializationIntention extends Intention { if (containingClass == null) { return; } - final boolean fieldIsStatic = - field.hasModifierProperty(PsiModifier.STATIC); - final PsiClassInitializer[] classInitializers = - containingClass.getInitializers(); + final boolean fieldIsStatic = field.hasModifierProperty(PsiModifier.STATIC); + final PsiClassInitializer[] classInitializers = containingClass.getInitializers(); PsiClassInitializer classInitializer = null; final int fieldOffset = field.getTextOffset(); for (PsiClassInitializer existingClassInitializer : classInitializers) { - final int initializerOffset = - existingClassInitializer.getTextOffset(); + final int initializerOffset = existingClassInitializer.getTextOffset(); if (initializerOffset <= fieldOffset) { continue; } - final boolean initializerIsStatic = - existingClassInitializer.hasModifierProperty( - PsiModifier.STATIC); + final boolean initializerIsStatic = existingClassInitializer.hasModifierProperty(PsiModifier.STATIC); if (initializerIsStatic == fieldIsStatic) { - classInitializer = existingClassInitializer; - break; + Predicate usedBeforeInitializer = ref -> { + PsiElement refElement = ref.getElement(); + if (refElement == null) { + return true; + } + TextRange textRange = refElement.getTextRange(); + return textRange == null || textRange.getStartOffset() < initializerOffset; + }; + if (ReferencesSearch.search(field, new LocalSearchScope(containingClass)).findAll().stream().noneMatch(usedBeforeInitializer)) { + classInitializer = existingClassInitializer; + break; + } } } final PsiManager manager = field.getManager(); final Project project = manager.getProject(); - final PsiElementFactory elementFactory = - JavaPsiFacade.getInstance(project).getElementFactory(); + final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); if (classInitializer == null) { classInitializer = elementFactory.createClassInitializer(); - classInitializer = (PsiClassInitializer) - containingClass.addAfter(classInitializer, field); + classInitializer = (PsiClassInitializer)containingClass.addAfter(classInitializer, field); // add some whitespace between the field and the class initializer - final PsiElement whitespace = - PsiParserFacade.SERVICE.getInstance(project).createWhiteSpaceFromText("\n"); + final PsiElement whitespace = PsiParserFacade.SERVICE.getInstance(project).createWhiteSpaceFromText("\n"); containingClass.addAfter(whitespace, field); } final PsiCodeBlock body = classInitializer.getBody(); - @NonNls final String initializationStatementText = - field.getName() + " = " + initializerText + ';'; - final PsiExpressionStatement statement = - (PsiExpressionStatement)elementFactory.createStatementFromText( - initializationStatementText, body); + @NonNls final String initializationStatementText = field.getName() + " = " + initializerText + ';'; + final PsiExpressionStatement statement = (PsiExpressionStatement)elementFactory.createStatementFromText(initializationStatementText, body); final PsiElement addedElement = body.add(statement); if (fieldIsStatic) { - final PsiModifierList modifierList = - classInitializer.getModifierList(); + final PsiModifierList modifierList = classInitializer.getModifierList(); if (modifierList != null) { modifierList.setModifierProperty(PsiModifier.STATIC, true); } } initializer.delete(); CodeStyleManager.getInstance(manager.getProject()).reformat(classInitializer); - HighlightUtil.highlightElement(addedElement, - IntentionPowerPackBundle.message( + HighlightUtil.highlightElement(addedElement, IntentionPowerPackBundle.message( "press.escape.to.remove.highlighting.message")); } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/VarargArgumentsPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/VarargArgumentsPredicate.java index bc9fc916519b..d2fb11276c48 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/VarargArgumentsPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/varargs/VarargArgumentsPredicate.java @@ -15,7 +15,9 @@ */ package com.siyeh.ipp.varargs; +import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import com.siyeh.ipp.base.PsiElementPredicate; import org.jetbrains.annotations.NotNull; @@ -48,29 +50,22 @@ class VarargArgumentsPredicate implements PsiElementPredicate { // "Unnecessarily qualified static usage" inspection // the psi gets into a bad state, this guards against that. // http://www.jetbrains.net/jira/browse/IDEADEV-40124 - final PsiReferenceExpression methodExpression = - methodCallExpression.getMethodExpression(); - final PsiExpression qualifier = - methodExpression.getQualifierExpression(); + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); if (qualifier == null) { - final PsiReferenceParameterList typeParameterList = - methodExpression.getParameterList(); + final PsiReferenceParameterList typeParameterList = methodExpression.getParameterList(); if (typeParameterList != null) { - final PsiTypeElement[] typeParameterElements = - typeParameterList.getTypeParameterElements(); + final PsiTypeElement[] typeParameterElements = typeParameterList.getTypeParameterElements(); if (typeParameterElements.length > 0) { return false; } } } - final PsiParameter[] parameters = parameterList.getParameters(); - final PsiParameter lastParameter = parameters[parameters.length - 1]; - final PsiEllipsisType lastParameterType = (PsiEllipsisType)lastParameter.getType(); - final PsiType lastType = lastParameterType.getComponentType(); final JavaResolveResult resolveResult = methodCallExpression.resolveMethodGenerics(); final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); - final PsiType substitutedType = substitutor.substitute(lastType); + PsiType lastParameterType = PsiTypesUtil.getParameterType(parameterList.getParameters(), parametersCount - 1, true); + final PsiType substitutedType = substitutor.substitute(lastParameterType); if (substitutedType instanceof PsiCapturedWildcardType) { final PsiCapturedWildcardType capturedWildcardType = (PsiCapturedWildcardType)substitutedType; if (!capturedWildcardType.getWildcard().isSuper()) { @@ -78,13 +73,15 @@ class VarargArgumentsPredicate implements PsiElementPredicate { return false; } } + + if (!JavaGenericsUtil.isReifiableType(substitutedType)) { + return false; + } if (arguments.length != parametersCount) { return true; } - final PsiExpression lastExpression = - arguments[arguments.length - 1]; - final PsiExpression expression = PsiUtil.deparenthesizeExpression( - lastExpression); + final PsiExpression lastExpression = arguments[arguments.length - 1]; + final PsiExpression expression = PsiUtil.deparenthesizeExpression(lastExpression); if (expression instanceof PsiLiteralExpression) { final String text = expression.getText(); if ("null".equals(text)) { diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_builder/ConstantRequiredInsideAnnotationMethod.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_builder/ConstantRequiredInsideAnnotationMethod.java new file mode 100644 index 000000000000..c69e724452dd --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/concatenation/string_builder/ConstantRequiredInsideAnnotationMethod.java @@ -0,0 +1,5 @@ +package com.siyeh.ipp.concatenation.string_builder; + +public @interface ConstantRequiredInsideAnnotationMethod { + String val() default "hey," + ""; +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock.java new file mode 100644 index 000000000000..bf1787f705eb --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock.java @@ -0,0 +1,5 @@ +class B { + { + Runnable r = () -> {} ; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock_after.java new file mode 100644 index 000000000000..8ab95092e18a --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/functional/extractToMethodReference/EmptyCodeBlock_after.java @@ -0,0 +1,8 @@ +class B { + { + Runnable r = B::run; + } + + private static void run() { + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer.java new file mode 100644 index 000000000000..594414f839bb --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer.java @@ -0,0 +1,8 @@ +class Foo { + static final int f1 = 2; + static final int f2 = f1 + 1; + + static { + System.out.println(); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer_after.java new file mode 100644 index 000000000000..1697906d1cb0 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/FieldUsedBeforeInitializer_after.java @@ -0,0 +1,13 @@ +class Foo { + static final int f1; + + static { + f1 = 2; + } + + static final int f2 = f1 + 1; + + static { + System.out.println(); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntentionTest.java new file mode 100644 index 000000000000..20d2035b6ae8 --- /dev/null +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithFormatStringIntentionTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2017 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.siyeh.ipp.concatenation; + +import com.siyeh.ipp.IPPTestCase; +import junit.framework.TestCase; + +/** + * @author Bas Leijdekkers + */ +public class ReplaceConcatenationWithFormatStringIntentionTest extends IPPTestCase { + + public void testNarrowingCast() { + doTest("class X {" + + " String s = (byte)321 +/*_Replace '+' with 'String.format()'*/ \" parsecs\";" + + "}", + + "class X {" + + " String s = String.format(\"%s parsecs\", (byte) 321);" + + "}" + ); + } + + public void testWideningCast() { + doTest("class X {" + + " String s = (long)42 /*_Replace '+' with 'String.format()'*/+ \" the answer to life, the universe and everything\";" + + "}", + + "class X {" + + " String s = String.format(\"%d the answer to life, the universe and everything\", 42);" + + "}"); + } + + public void testCastToChar() { + doTest("class X {" + + " String deepThought(byte b) {" + + " return (char)b/*_Replace '+' with 'String.format()'*/ + \" the answer to life, the universe and everything\";" + + " }" + + "}", + + "class X {" + + " String deepThought(byte b) {" + + " return String.format(\"%s the answer to life, the universe and everything\", (char) b);" + + " }" + + "}"); + } + +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithStringBufferIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithStringBufferIntentionTest.java index 82ee9ca4b93e..3baa064bf8c8 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithStringBufferIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/concatenation/ReplaceConcatenationWithStringBufferIntentionTest.java @@ -10,6 +10,7 @@ public class ReplaceConcatenationWithStringBufferIntentionTest extends IPPTestCa public void testNonStringConcatenationStart() { doTest(); } public void testConcatenationInsideAppend() { doTest(); } + public void testConstantRequiredInsideAnnotationMethod() { assertIntentionNotAvailable(); } @Override protected String getIntentionName() { diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/functional/ExtractToMethodReferenceTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/functional/ExtractToMethodReferenceTest.java index 5be4b04af3bf..c594a70339ac 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/functional/ExtractToMethodReferenceTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/functional/ExtractToMethodReferenceTest.java @@ -54,6 +54,10 @@ public class ExtractToMethodReferenceTest extends IPPTestCase { assertIntentionNotAvailable(); } + public void testEmptyCodeBlock() throws Exception { + doTest(); + } + public void testUsedLocalVariables() throws Exception { assertIntentionNotAvailable(); } diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java index 933d5e2e7964..e8c5659abdd4 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java @@ -26,6 +26,7 @@ public class SplitDeclarationAndInitializationIntentionTest extends IPPTestCase public void testArrayInitializer() { doTest(); } public void testArray() { doTest(); } + public void testFieldUsedBeforeInitializer() { doTest(); } @Override protected String getRelativePath() { diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/vararg/WrapVarargArgumentsWithExplicitArrayIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/vararg/WrapVarargArgumentsWithExplicitArrayIntentionTest.java index fc355c2f8579..4f811c316cc4 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/vararg/WrapVarargArgumentsWithExplicitArrayIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/vararg/WrapVarargArgumentsWithExplicitArrayIntentionTest.java @@ -58,4 +58,16 @@ public class WrapVarargArgumentsWithExplicitArrayIntentionTest extends IPPTestCa " }\n" + "}"); } + + public void testNonReifiable() { + doTestIntentionNotAvailable( + "" + + "import java.util.*;\n" + + "class Y {\n" + + " void m(Set... t){}\n" + + " public static void run(Set s) {\n" + + " m(/*_Wrap vararg arguments with explicit array creation*/s);\n" + + " }\n" + + "}"); + } } diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index 038ac6e059b4..a6d53798ac1a 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -368,8 +368,9 @@ public class GitUtil { public static VirtualFile getGitRootOrNull(@NotNull final FilePath filePath) { File root = filePath.getIOFile(); while (root != null) { - File gitDir = findGitDir(root); - if (gitDir != null) return LocalFileSystem.getInstance().findFileByIoFile(root); + if (isGitRoot(root)) { + return LocalFileSystem.getInstance().findFileByIoFile(root); + } root = root.getParentFile(); } return null; diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java index 30f02e80538d..7cdc4ee0930e 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java @@ -23,6 +23,7 @@ import com.intellij.dvcs.ui.RootAction; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.EmptyAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.util.containers.ContainerUtil; @@ -34,7 +35,6 @@ import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import java.util.List; import java.util.Objects; @@ -43,6 +43,7 @@ import static com.intellij.dvcs.branch.DvcsBranchPopup.MyMoreIndex.MAX_NUM; import static com.intellij.dvcs.ui.BranchActionGroupPopup.wrapWithMoreActionIfNeeded; import static com.intellij.dvcs.ui.BranchActionUtil.FAVORITE_BRANCH_COMPARATOR; import static com.intellij.dvcs.ui.BranchActionUtil.getNumOfTopShownBranches; +import static com.intellij.util.ObjectUtils.tryCast; import static com.intellij.util.containers.ContainerUtil.map; import static java.util.stream.Collectors.toList; @@ -64,8 +65,8 @@ class GitBranchPopup extends DvcsBranchPopup { static GitBranchPopup getInstance(@NotNull final Project project, @NotNull GitRepository currentRepository) { final GitVcsSettings vcsSettings = GitVcsSettings.getInstance(project); Condition preselectActionCondition = action -> { - if (action instanceof GitBranchPopupActions.LocalBranchActions) { - GitBranchPopupActions.LocalBranchActions branchAction = (GitBranchPopupActions.LocalBranchActions)action; + GitBranchPopupActions.LocalBranchActions branchAction = getBranchAction(action); + if (branchAction != null) { String branchName = branchAction.getBranchName(); String recentBranch; @@ -86,6 +87,13 @@ class GitBranchPopup extends DvcsBranchPopup { return new GitBranchPopup(currentRepository, GitUtil.getRepositoryManager(project), vcsSettings, preselectActionCondition); } + @Nullable + private static GitBranchPopupActions.LocalBranchActions getBranchAction(@NotNull AnAction action) { + AnAction resultAction = + action instanceof EmptyAction.MyDelegatingActionGroup ? ((EmptyAction.MyDelegatingActionGroup)action).getDelegate() : action; + return tryCast(resultAction, GitBranchPopupActions.LocalBranchActions.class); + } + private GitBranchPopup(@NotNull GitRepository currentRepository, @NotNull GitRepositoryManager repositoryManager, @NotNull GitVcsSettings vcsSettings, @@ -94,24 +102,6 @@ class GitBranchPopup extends DvcsBranchPopup { preselectActionCondition, DIMENSION_SERVICE_KEY); } - @Override - protected void setCurrentBranchInfo() { - String currentBranchText = "Current branch"; - if (myRepositoryManager.moreThanOneRoot()) { - if (myMultiRootBranchConfig.diverged()) { - currentBranchText += " in " + DvcsUtil.getShortRepositoryName(myCurrentRepository) + ": " + - GitBranchUtil.getDisplayableBranchText(myCurrentRepository); - } - else { - currentBranchText += ": " + myMultiRootBranchConfig.getCurrentBranch(); - } - } - else { - currentBranchText += ": " + GitBranchUtil.getDisplayableBranchText(myCurrentRepository); - } - myPopup.setAdText(currentBranchText, SwingConstants.CENTER); - } - @Override protected void fillWithCommonRepositoryActions(@NotNull DefaultActionGroup popupGroup, @NotNull AbstractRepositoryManager repositoryManager) { @@ -122,11 +112,19 @@ class GitBranchPopup extends DvcsBranchPopup { popupGroup.addAll(createRepositoriesActions()); popupGroup.addSeparator("Common Local Branches"); - List localBranchActions = - myMultiRootBranchConfig.getLocalBranchNames().stream().map(l -> createLocalBranchActions(allRepositories, l)).filter(Objects::nonNull) - .collect(toList()); - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(localBranchActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(localBranchActions), SHOW_ALL_LOCALS_KEY, true); + List localBranchActions = myMultiRootBranchConfig.getLocalBranchNames().stream() + .map(l -> createLocalBranchActions(allRepositories, l)) + .filter(Objects::nonNull) + .sorted(FAVORITE_BRANCH_COMPARATOR) + .collect(toList()); + int topShownBranches = getNumOfTopShownBranches(localBranchActions); + String currentBranch = myMultiRootBranchConfig.getCurrentBranch(); + if (currentBranch != null) { + localBranchActions + .add(0, new GitBranchPopupActions.CurrentBranchActions(myProject, allRepositories, currentBranch, myCurrentRepository)); + topShownBranches++; + } + wrapWithMoreActionIfNeeded(myProject, popupGroup, localBranchActions, topShownBranches, SHOW_ALL_LOCALS_KEY, true); popupGroup.addSeparator("Common Remote Branches"); List remoteBranchActions = map(((GitMultiRootBranchConfig)myMultiRootBranchConfig).getRemoteBranches(), @@ -154,7 +152,7 @@ class GitBranchPopup extends DvcsBranchPopup { popupGroup.addSeparator("Repositories"); List rootActions = DvcsUtil.sortRepositories(myRepositoryManager.getRepositories()).stream() .map(repo -> new RootAction<>(repo, new GitBranchPopupActions(repo.getProject(), repo).createActions(), - GitBranchUtil.getDisplayableBranchText(repo))).collect(toList()); + isBranchesDiverged() ? GitBranchUtil.getDisplayableBranchText(repo) : null)).collect(toList()); wrapWithMoreActionIfNeeded(myProject, popupGroup, rootActions, rootActions.size() > MAX_NUM ? DEFAULT_NUM : MAX_NUM, SHOW_ALL_REPOSITORIES); return popupGroup; diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java index 3aff81024041..8012860e3ac3 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java @@ -27,6 +27,7 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.util.containers.ContainerUtil; +import git4idea.GitLocalBranch; import git4idea.branch.GitBranchUtil; import git4idea.branch.GitBrancher; import git4idea.branch.GitNewBranchOptions; @@ -74,15 +75,21 @@ class GitBranchPopupActions { } popupGroup.addSeparator("Local Branches" + repoInfo); - List localBranchActions = - myRepository.getBranches().getLocalBranches().stream() - .sorted() - .filter(branch -> !branch.equals(myRepository.getCurrentBranch())) - .map(branch -> new LocalBranchActions(myProject, repositoryList, branch.getName(), myRepository)) - .collect(toList()); + GitLocalBranch currentBranch = myRepository.getCurrentBranch(); + List localBranchActions = myRepository.getBranches().getLocalBranches().stream() + .sorted() + .filter(branch -> !branch.equals(currentBranch)) + .map(branch -> new LocalBranchActions(myProject, repositoryList, branch.getName(), myRepository)) + .sorted(FAVORITE_BRANCH_COMPARATOR) + .collect(toList()); + int topShownBranches = getNumOfTopShownBranches(localBranchActions); + if (currentBranch != null) { + localBranchActions.add(0, new CurrentBranchActions(myProject, repositoryList, currentBranch.getName(), myRepository)); + topShownBranches++; + } // if there are only a few local favorites -> show all; for remotes it's better to show only favorites; - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(localBranchActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(localBranchActions), firstLevelGroup ? GitBranchPopup.SHOW_ALL_LOCALS_KEY : null, + wrapWithMoreActionIfNeeded(myProject, popupGroup, localBranchActions, + topShownBranches, firstLevelGroup ? GitBranchPopup.SHOW_ALL_LOCALS_KEY : null, firstLevelGroup); popupGroup.addSeparator("Remote Branches" + repoInfo); @@ -160,9 +167,9 @@ class GitBranchPopupActions { */ static class LocalBranchActions extends BranchActionGroup implements PopupElementWithAdditionalInfo { - private final Project myProject; - private final List myRepositories; - private final String myBranchName; + protected final Project myProject; + protected final List myRepositories; + protected final String myBranchName; @NotNull private final GitRepository mySelectedRepository; private final GitBranchManager myGitBranchManager; @@ -310,6 +317,27 @@ class GitBranchPopupActions { } } + static class CurrentBranchActions extends LocalBranchActions { + CurrentBranchActions(@NotNull Project project, + @NotNull List repositories, + @NotNull String branchName, + @NotNull GitRepository selectedRepository) { + super(project, repositories, branchName, selectedRepository); + } + + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return new AnAction[]{new LocalBranchActions.RenameBranchAction(myProject, myRepositories, myBranchName)}; + } + + @Nullable + @Override + public String getPrefixInfo() { + return "current"; + } + } + /** * Actions available for remote branches */ diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java index 25c1382e4555..599cb8c84d35 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java @@ -978,6 +978,39 @@ public class GradleDependenciesImportingTest extends GradleImportingTestCase { } } + @Test + @TargetVersions("2.12+") + public void testCompileOnlyAndCompileScope() throws Exception { + createSettingsFile("include 'app'\n"); + importProject( + "apply plugin: 'java'\n" + + "dependencies {\n" + + " compileOnly project(':app')\n" + + " compile 'junit:junit:4.11'\n" + + "}\n" + + "project(':app') {\n" + + " apply plugin: 'java'\n" + + " repositories {\n" + + " mavenCentral()\n" + + " }\n" + + " dependencies {\n" + + " compile 'junit:junit:4.11'\n" + + " }\n" + + "}" + ); + + assertModules("project", "project_main", "project_test", "app", "app_main", "app_test"); + + assertModuleModuleDepScope("project_main", "app_main", DependencyScope.PROVIDED); + assertModuleLibDepScope("project_main", "Gradle: org.hamcrest:hamcrest-core:1.3", DependencyScope.COMPILE); + assertModuleLibDepScope("project_main", "Gradle: junit:junit:4.11", DependencyScope.COMPILE); + + assertModuleModuleDeps("project_test", "project_main"); + assertModuleModuleDepScope("project_test", "project_main", DependencyScope.COMPILE); + assertModuleLibDepScope("project_test", "Gradle: junit:junit:4.11", DependencyScope.COMPILE); + assertModuleLibDepScope("project_test", "Gradle: org.hamcrest:hamcrest-core:1.3", DependencyScope.COMPILE); + } + @Test @TargetVersions("3.4+") public void testJavaLibraryPluginConfigurations() throws Exception { diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy index f8fb31faf26d..2af733b27af4 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy @@ -32,7 +32,6 @@ import org.gradle.api.artifacts.component.ModuleComponentIdentifier import org.gradle.api.artifacts.component.ModuleComponentSelector import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.artifacts.component.ProjectComponentSelector -import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.artifacts.result.* import org.gradle.api.plugins.WarPlugin import org.gradle.api.specs.Specs @@ -44,6 +43,7 @@ import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.language.base.artifact.SourcesArtifact import org.gradle.language.java.artifact.JavadocArtifact import org.gradle.plugins.ide.idea.IdeaPlugin +import org.gradle.util.GUtil import org.gradle.util.GradleVersion import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.Nullable @@ -210,6 +210,9 @@ class DependencyResolverImpl implements DependencyResolver { Collection result = new ArrayList<>() // resolve compile dependencies + def isMainSourceSet = sourceSet.name == SourceSet.MAIN_SOURCE_SET_NAME + String deprecatedCompileConfigurationName = isMainSourceSet ? "compile" : GUtil.toCamelCase(sourceSet.name) + "Compile" + def deprecatedCompileConfiguration = myProject.configurations.findByName(deprecatedCompileConfigurationName) def compileConfigurationName = sourceSet.compileConfigurationName def compileClasspathConfiguration = myProject.configurations.findByName(compileConfigurationName + 'Classpath') def originCompileConfiguration = myProject.configurations.findByName(compileConfigurationName) @@ -238,17 +241,17 @@ class DependencyResolverImpl implements DependencyResolver { // since version 3.4 compileOnly no longer extends compile // so, we can use compileOnly configuration for the check + Object[] resolvedObjArray = resolvedObj instanceof Collection ? ((Collection)resolvedObj).toArray() : [resolvedObj] if (isJavaLibraryPluginSupported) { - if (compileOnlyConfiguration != null && - (resolvedObj instanceof Collection ? compileOnlyConfiguration.containsAll(((Collection)resolvedObj).toArray()) : - compileOnlyConfiguration.contains(resolvedObj))) { - ((AbstractExternalDependency)it).scope = providedScope + if (compileOnlyConfiguration != null && compileOnlyConfiguration.containsAll(resolvedObjArray)) { + // deprecated 'compile' configuration still can be used + if (deprecatedCompileConfiguration == null || !deprecatedCompileConfiguration.containsAll(resolvedObjArray)) { + ((AbstractExternalDependency)it).scope = providedScope + } } } else { - if (checkCompileOnlyDeps && - (resolvedObj instanceof Collection ? !originCompileConfiguration.containsAll(((Collection)resolvedObj).toArray()) : - !originCompileConfiguration.contains(resolvedObj))) { + if (checkCompileOnlyDeps && !originCompileConfiguration.containsAll(resolvedObjArray)) { ((AbstractExternalDependency)it).scope = providedScope } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java index 24eb5814566b..93951d98277a 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -22,6 +22,7 @@ import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.findUsages.LiteralConstructorReference; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; @@ -116,7 +117,7 @@ public class GrListOrMapInfo implements ConstructorCallInfo { if (type == null) return GroovyResolveResult.EMPTY_ARRAY; final GroovyResolveResult result = GroovyResolveResultImpl.from(type.resolveGenerics()); - if (result == GroovyResolveResult.EMPTY_RESULT) return GroovyResolveResult.EMPTY_ARRAY; + if (result == EmptyGroovyResolveResult.INSTANCE) return GroovyResolveResult.EMPTY_ARRAY; return new GroovyResolveResult[]{result}; } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessChecker.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessChecker.java index 4d059172b88c..a50ae423a2e6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessChecker.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessChecker.java @@ -50,6 +50,7 @@ import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; @@ -389,7 +390,7 @@ public class GrUnresolvedAccessChecker { @NotNull private static GroovyResolveResult getBestResolveResult(GrReferenceExpression ref) { GroovyResolveResult[] results = ref.multiResolve(false); - if (results.length == 0) return GroovyResolveResult.EMPTY_RESULT; + if (results.length == 0) return EmptyGroovyResolveResult.INSTANCE; if (results.length == 1) return results[0]; for (GroovyResolveResult result : results) { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyApplicabilityProvider.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyApplicabilityProvider.java new file mode 100644 index 000000000000..cf42409d8edb --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyApplicabilityProvider.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2017 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 org.jetbrains.plugins.groovy.extensions; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiSubstitutor; +import com.intellij.psi.PsiType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil.ApplicabilityResult; + +public abstract class GroovyApplicabilityProvider { + + public static final ExtensionPointName EP_NAME = + ExtensionPointName.create("org.intellij.groovy.applicabilityProvider"); + + /** + * @return null if provider could not be applied in this case + */ + @Nullable + public abstract ApplicabilityResult isApplicable(@NotNull PsiType[] argumentTypes, + @NotNull PsiMethod method, + @Nullable PsiSubstitutor substitutor, + @Nullable PsiElement place, + final boolean eraseParameterTypes); + + @Nullable + public static ApplicabilityResult checkProviders(@NotNull PsiType[] argumentTypes, + @NotNull PsiMethod method, + @Nullable PsiSubstitutor substitutor, + @Nullable PsiElement place, + final boolean eraseParameterTypes) { + for (GroovyApplicabilityProvider applicabilityProvider : EP_NAME.getExtensions()) { + ApplicabilityResult result = applicabilityProvider.isApplicable(argumentTypes, method, substitutor, place, eraseParameterTypes); + if (result != null) return result; + } + return null; + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java index 77c1f975c74f..cc58a9cc5925 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentProvider.java @@ -20,6 +20,7 @@ import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; @@ -79,8 +80,8 @@ public abstract class GroovyNamedArgumentProvider { @Nullable public static Map getNamedArgumentsFromAllProviders(@NotNull GrCall call, - @Nullable String argumentName, - boolean forCompletion) { + @Nullable String argumentName, + boolean forCompletion) { Map namedArguments = new HashMap() { @Override public NamedArgumentDescriptor put(String key, NamedArgumentDescriptor value) { @@ -98,21 +99,23 @@ public abstract class GroovyNamedArgumentProvider { if (callVariants.length == 0 || PsiUtil.isSingleBindingVariant(callVariants)) { for (GroovyNamedArgumentProvider namedArgumentProvider : EP_NAME.getExtensions()) { - namedArgumentProvider.getNamedArguments(call, GroovyResolveResult.EMPTY_RESULT, argumentName, forCompletion, namedArguments); + namedArgumentProvider.getNamedArguments(call, EmptyGroovyResolveResult.INSTANCE, argumentName, forCompletion, namedArguments); } } else { boolean mapExpected = false; for (GroovyResolveResult result : callVariants) { + for (GroovyNamedArgumentProvider namedArgumentProvider : EP_NAME.getExtensions()) { + namedArgumentProvider.getNamedArguments(call, result, argumentName, forCompletion, namedArguments); + } PsiElement element = result.getElement(); if (element instanceof GrAccessorMethod) continue; if (element instanceof PsiMethod) { PsiMethod method = (PsiMethod)element; - PsiMethod mirror = PsiUtil.handleMirrorMethod(method); PsiParameter[] parameters = method.getParameterList().getParameters(); - if (!mirror.isConstructor() && !(parameters.length > 0 && canBeMap(parameters[0]))) continue; + if (!method.isConstructor() && !(parameters.length > 0 && canBeMap(parameters[0]))) continue; mapExpected = true; @@ -130,10 +133,6 @@ public abstract class GroovyNamedArgumentProvider { } } - for (GroovyNamedArgumentProvider namedArgumentProvider : EP_NAME.getExtensions()) { - namedArgumentProvider.getNamedArguments(call, result, argumentName, forCompletion, namedArguments); - } - if (element instanceof GrVariable && InheritanceUtil.isInheritor(((GrVariable)element).getTypeGroovy(), GroovyCommonClassNames.GROOVY_LANG_CLOSURE)) { mapExpected = true; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java index 014ebd44aad6..2d55cc739841 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -25,6 +25,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; @@ -190,7 +191,7 @@ public class LiteralConstructorReference extends PsiReferenceBase.Poly getCorrespondingClasses(@NotNull GrCall call, @NotNull GroovyResolveResult resolveResult); + public abstract List getCorrespondingClasses(@NotNull GrCall call, @NotNull GroovyResolveResult resolveResult); @Override public void getNamedArguments(@NotNull GrCall call, @@ -74,7 +74,6 @@ public abstract class GroovyConstructorNamedArgumentProvider extends GroovyNamed processClass(call, classType, argumentName, result); } - } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewExprNamedArgumentProvider.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewExprNamedArgumentProvider.kt index ffe6eaf95967..b2533d265c1f 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewExprNamedArgumentProvider.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewExprNamedArgumentProvider.kt @@ -23,7 +23,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExp class GroovyNewExprNamedArgumentProvider : GroovyConstructorNamedArgumentProvider() { - internal override fun getCorrespondingClasses(call: GrCall, resolveResult: GroovyResolveResult): List { + override fun getCorrespondingClasses(call: GrCall, resolveResult: GroovyResolveResult): List { val newExpr = call as? GrNewExpression ?: return emptyList() val resolve = resolveResult.element (resolve as? PsiMethod)?.let { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GrReferenceElement.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GrReferenceElement.java index d2e19cc8d50e..28a74e11984c 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GrReferenceElement.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GrReferenceElement.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -16,18 +16,17 @@ package org.jetbrains.plugins.groovy.lang.psi; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeArgumentList; -import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; /** * @author ven */ -public interface GrReferenceElement extends GroovyPsiElement, PsiPolyVariantReference, GrQualifiedReference { +public interface GrReferenceElement extends GroovyPsiElement, GroovyPolyVariantReference, GrQualifiedReference { + @Override @Nullable String getReferenceName(); @@ -38,15 +37,6 @@ public interface GrReferenceElement extends GroovyPsiEleme return advancedResolve().getElement(); } - @NotNull - default GroovyResolveResult advancedResolve() { - return PsiImplUtil.extractUniqueResult(multiResolve(false)); - } - - @Override - @NotNull - GroovyResolveResult[] multiResolve(boolean incompleteCode); - @NotNull PsiType[] getTypeArguments(); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/EmptyGroovyResolveResult.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/EmptyGroovyResolveResult.kt new file mode 100644 index 000000000000..81ecdace5a70 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/EmptyGroovyResolveResult.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 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 org.jetbrains.plugins.groovy.lang.psi.api + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiSubstitutor + +object EmptyGroovyResolveResult : GroovyResolveResult { + + override fun getElement(): PsiElement? = null + + override fun isApplicable(): Boolean = false + + override fun isAccessible(): Boolean = false + + override fun getCurrentFileResolveContext(): PsiElement? = null + + override fun isStaticsOK(): Boolean = true + + override fun getSubstitutor(): PsiSubstitutor = PsiSubstitutor.EMPTY + + override fun isValidResult(): Boolean = false + + override fun isInvokedOnProperty(): Boolean = false + + override fun getSpreadState(): SpreadState? = null +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/GroovyPolyVariantReference.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/GroovyPolyVariantReference.java similarity index 80% rename from plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/GroovyPolyVariantReference.java rename to plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/GroovyPolyVariantReference.java index 8fe3740006eb..deb7cb2fa289 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/GroovyPolyVariantReference.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/GroovyPolyVariantReference.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.plugins.groovy.lang.resolve; +package org.jetbrains.plugins.groovy.lang.psi.api; import com.intellij.psi.PsiPolyVariantReference; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; /** * Same as {@link PsiPolyVariantReference} but returns {@link GroovyResolveResult}. @@ -29,4 +29,9 @@ public interface GroovyPolyVariantReference extends PsiPolyVariantReference { @NotNull @Override GroovyResolveResult[] multiResolve(boolean incompleteCode); + + @NotNull + default GroovyResolveResult advancedResolve() { + return PsiImplUtil.extractUniqueResult(multiResolve(false)); + } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/GroovyResolveResult.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/GroovyResolveResult.java index 5bfc8d09dd3d..d9e793d5a6ce 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/GroovyResolveResult.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/GroovyResolveResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -25,6 +25,10 @@ import org.jetbrains.annotations.Nullable; * @author ven */ public interface GroovyResolveResult extends ResolveResult { + + @Deprecated + GroovyResolveResult EMPTY_RESULT = EmptyGroovyResolveResult.INSTANCE; + GroovyResolveResult[] EMPTY_ARRAY = new GroovyResolveResult[0]; boolean isAccessible(); @@ -43,53 +47,4 @@ public interface GroovyResolveResult extends ResolveResult { @Nullable SpreadState getSpreadState(); - - GroovyResolveResult EMPTY_RESULT = new GroovyResolveResult() { - @Override - public boolean isAccessible() { - return false; - } - - @Override - public PsiElement getCurrentFileResolveContext() { - return null; - } - - @Override - public boolean isStaticsOK() { - return true; - } - - @Override - public boolean isApplicable() { - return false; - } - - @Override - @NotNull - public PsiSubstitutor getSubstitutor() { - return PsiSubstitutor.EMPTY; - } - - @Override - @Nullable - public PsiElement getElement() { - return null; - } - - @Override - public boolean isValidResult() { - return false; - } - - @Override - public boolean isInvokedOnProperty() { - return false; - } - - @Override - public SpreadState getSpreadState() { - return null; - } - }; } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrArgumentLabel.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrArgumentLabel.java index 885042c40ebb..9d06b4a170ff 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrArgumentLabel.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrArgumentLabel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -17,30 +17,29 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.PsiType; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; /** * @author ilyas */ -public interface GrArgumentLabel extends GroovyPsiElement, PsiPolyVariantReference { +public interface GrArgumentLabel extends GroovyPsiElement, GroovyPolyVariantReference { GrArgumentLabel[] EMPTY_ARRAY = new GrArgumentLabel[0]; @NotNull PsiElement getNameElement(); - @Nullable /** - * returns expression which is put into parentheses. + * @return expression which is put into parentheses. */ + @Nullable GrExpression getExpression(); @Nullable @@ -55,11 +54,4 @@ public interface GrArgumentLabel extends GroovyPsiElement, PsiPolyVariantReferen PsiType getLabelType(); GrNamedArgument getNamedArgument(); - - @Override - @NotNull - GroovyResolveResult[] multiResolve(boolean incomplete); - - @NotNull - GroovyResolveResult advancedResolve(); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java index 0854410cd381..7787f995bfa8 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -16,14 +16,13 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.PsiType; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; -public interface GrOperatorExpression extends GrExpression, PsiPolyVariantReference { +public interface GrOperatorExpression extends GrExpression, GroovyPolyVariantReference { @Nullable PsiType getLeftType(); @@ -36,7 +35,4 @@ public interface GrOperatorExpression extends GrExpression, PsiPolyVariantRefere @NotNull IElementType getOperationTokenType(); - - @NotNull - GroovyResolveResult[] multiResolve(final boolean incompleteCode); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrSafeCastExpression.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrSafeCastExpression.java index 36ea0ff91741..59c1e7a89a43 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrSafeCastExpression.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrSafeCastExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -17,16 +17,16 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; /** * @author ven */ -public interface GrSafeCastExpression extends GrExpression, PsiPolyVariantReference { +public interface GrSafeCastExpression extends GrExpression, GroovyPolyVariantReference { + @Nullable GrTypeElement getCastTypeElement(); @@ -35,8 +35,4 @@ public interface GrSafeCastExpression extends GrExpression, PsiPolyVariantRefere @NotNull PsiElement getOperationToken(); - - @NotNull - @Override - GroovyResolveResult[] multiResolve(final boolean incompleteCode); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java index ee4bc08ed674..68f92f37510b 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -17,16 +17,16 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; /** * @author ilyas */ -public interface GrUnaryExpression extends GrExpression, PsiPolyVariantReference { +public interface GrUnaryExpression extends GrExpression, GroovyPolyVariantReference { + @NotNull IElementType getOperationTokenType(); @@ -36,9 +36,5 @@ public interface GrUnaryExpression extends GrExpression, PsiPolyVariantReference @Nullable GrExpression getOperand(); - @NotNull - @Override - GroovyResolveResult[] multiResolve(final boolean incompleteCode); - boolean isPostfix(); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/path/GrIndexProperty.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/path/GrIndexProperty.java index de2db7261b58..70a9b916871e 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/path/GrIndexProperty.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/path/GrIndexProperty.java @@ -18,9 +18,9 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; -import org.jetbrains.plugins.groovy.lang.resolve.GroovyPolyVariantReference; public interface GrIndexProperty extends GrExpression { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyResolveResultImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyResolveResultImpl.java index ee8ac601200f..92d1c76dea43 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyResolveResultImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyResolveResultImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -21,6 +21,7 @@ import com.intellij.psi.PsiNamedElement; import com.intellij.psi.PsiSubstitutor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState; @@ -153,7 +154,7 @@ public class GroovyResolveResultImpl implements GroovyResolveResult { @NotNull public static GroovyResolveResult from(@NotNull PsiClassType.ClassResolveResult classResolveResult) { - if (classResolveResult.getElement() == null) return GroovyResolveResult.EMPTY_RESULT; + if (classResolveResult.getElement() == null) return EmptyGroovyResolveResult.INSTANCE; return new GroovyResolveResultImpl( classResolveResult.getElement(), null, diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java index 89da9021a604..38eb97f09b3e 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java @@ -44,6 +44,7 @@ import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; import org.jetbrains.plugins.groovy.lang.psi.*; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrCondition; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; @@ -338,7 +339,7 @@ public class PsiImplUtil { @NotNull public static GroovyResolveResult extractUniqueResult(@NotNull GroovyResolveResult[] results) { - if (results.length != 1) return GroovyResolveResult.EMPTY_RESULT; + if (results.length != 1) return EmptyGroovyResolveResult.INSTANCE; return results[0]; } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java index c6fb8579b652..52a56dcac9e7 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -33,6 +33,7 @@ import org.jetbrains.plugins.groovy.extensions.NamedArgumentUtilKt; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; @@ -206,7 +207,7 @@ public class GrArgumentLabelImpl extends GroovyPsiElementImpl implements GrArgum ResolveResult result = results[i]; final PsiElement element = result.getElement(); if (element == null) { - results1[i] = GroovyResolveResult.EMPTY_RESULT; + results1[i] = EmptyGroovyResolveResult.INSTANCE; } else { results1[i] = new GroovyResolveResultImpl(element, true); @@ -216,12 +217,6 @@ public class GrArgumentLabelImpl extends GroovyPsiElementImpl implements GrArgum } } - @NotNull - @Override - public GroovyResolveResult advancedResolve() { - return PsiImplUtil.extractUniqueResult(multiResolve(false)); - } - @Override @NotNull public String getCanonicalText() { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java index d2a2c988eaf9..36caedf0fa86 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java @@ -25,6 +25,7 @@ import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty; @@ -32,7 +33,6 @@ import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyIndexPropertyUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyLValueUtil; -import org.jetbrains.plugins.groovy.lang.resolve.GroovyPolyVariantReference; import org.jetbrains.plugins.groovy.lang.typing.GrTypeCalculator; /** diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyReference.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyReference.kt index 5675a3644dfd..7e15a11b7b0a 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyReference.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyReference.kt @@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.path import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiPolyVariantReferenceBase import com.intellij.psi.PsiType +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyPolyVariantReference import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty @@ -26,7 +27,6 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType import org.jetbrains.plugins.groovy.lang.psi.util.getArgumentListType import org.jetbrains.plugins.groovy.lang.psi.util.isClassLiteral import org.jetbrains.plugins.groovy.lang.psi.util.isSimpleArrayAccess -import org.jetbrains.plugins.groovy.lang.resolve.GroovyPolyVariantReference import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil class GrIndexPropertyReference(element: GrIndexPropertyImpl, val rhs: Boolean) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java index c5da5cc7b688..c9a82dd6e093 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -17,11 +17,9 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.path; import com.intellij.lang.ASTNode; -import com.intellij.psi.PsiElement; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; @@ -31,8 +29,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrC import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrMethodCallImpl; -import java.util.List; - /** * @author ilyas */ @@ -78,7 +74,6 @@ public class GrMethodCallExpressionImpl extends GrMethodCallImpl implements GrMe @Override @NotNull public GrClosableBlock[] getClosureArguments() { - final List children = findChildrenByType(GroovyElementTypes.CLOSABLE_BLOCK); - return children.toArray(new GrClosableBlock[children.size()]); + return findChildrenByClass(GrClosableBlock.class); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/types/GrSafeCastExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/types/GrSafeCastExpressionImpl.java index ba158bc4c38b..a1d48adbafc0 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/types/GrSafeCastExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/types/GrSafeCastExpressionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -45,7 +45,7 @@ import java.util.HashMap; /** * @author ven */ -public class GrSafeCastExpressionImpl extends GrExpressionImpl implements GrSafeCastExpression, PsiPolyVariantReference { +public class GrSafeCastExpressionImpl extends GrExpressionImpl implements GrSafeCastExpression { private static final Function TYPE_CALCULATOR = (NullableFunction)cast -> { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index 965ef543eb6d..6e1850d55269 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -40,11 +40,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; import org.jetbrains.plugins.groovy.config.GroovyConfigUtils; +import org.jetbrains.plugins.groovy.extensions.GroovyApplicabilityProvider; import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment; import org.jetbrains.plugins.groovy.lang.lexer.GroovyLexer; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; import org.jetbrains.plugins.groovy.lang.psi.*; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; @@ -165,7 +167,7 @@ public class PsiUtil { } public static boolean isApplicable(@Nullable PsiType[] argumentTypes, - PsiMethod method, + @NotNull PsiMethod method, PsiSubstitutor substitutor, PsiElement place, final boolean eraseParameterTypes) { @@ -174,30 +176,18 @@ public class PsiUtil { } public static GrClosureSignatureUtil.ApplicabilityResult isApplicableConcrete(@Nullable PsiType[] argumentTypes, - PsiMethod method, + @NotNull PsiMethod method, PsiSubstitutor substitutor, PsiElement place, final boolean eraseParameterTypes) { if (argumentTypes == null) return GrClosureSignatureUtil.ApplicabilityResult.canBeApplicable; GrClosureSignature signature = GrClosureSignatureUtil.createSignature(method, substitutor, eraseParameterTypes); - //check for default constructor - if (method.isConstructor() || handleMirrorMethod(method).isConstructor()) { - final PsiParameter[] parameters = method.getParameterList().getParameters(); - if (parameters.length == 0 && argumentTypes.length == 1) { - return InheritanceUtil.isInheritor(argumentTypes[0], CommonClassNames.JAVA_UTIL_MAP) - ? GrClosureSignatureUtil.ApplicabilityResult.applicable - : GrClosureSignatureUtil.ApplicabilityResult.inapplicable; - } - if (parameters.length == 1 && - argumentTypes.length == 0 && - InheritanceUtil.isInheritor(parameters[0].getType(), CommonClassNames.JAVA_UTIL_MAP)) { - return GrClosureSignatureUtil.ApplicabilityResult.inapplicable; - } - } - LOG.assertTrue(signature != null); GrClosureSignatureUtil.ApplicabilityResult result = - GrClosureSignatureUtil.isSignatureApplicableConcrete(signature, argumentTypes, place); + GroovyApplicabilityProvider.checkProviders(argumentTypes, method, substitutor, place, eraseParameterTypes); + if (result != null) return result; + + result = GrClosureSignatureUtil.isSignatureApplicableConcrete(signature, argumentTypes, place); if (result != GrClosureSignatureUtil.ApplicabilityResult.inapplicable) { return result; } @@ -212,17 +202,6 @@ public class PsiUtil { return GrClosureSignatureUtil.ApplicabilityResult.inapplicable; } - @NotNull - public static PsiMethod handleMirrorMethod(@NotNull PsiMethod method) { - if (method instanceof PsiMirrorElement) { - PsiElement prototype = ((PsiMirrorElement)method).getPrototype(); - if (prototype instanceof PsiMethod) { - return (PsiMethod)prototype; - } - } - return method; - } - public static boolean isApplicable(@Nullable PsiType[] argumentTypes, GrClosureType type, GroovyPsiElement context) { @@ -1254,7 +1233,7 @@ public class PsiUtil { @NotNull public static ResolveResult getAccessObjectClass(GrExpression expression) { - if (isThisOrSuperRef(expression)) return GroovyResolveResult.EMPTY_RESULT; + if (isThisOrSuperRef(expression)) return EmptyGroovyResolveResult.INSTANCE; PsiType type = expression.getType(); if (type instanceof PsiClassType) { return ((PsiClassType)type).resolveGenerics(); @@ -1265,7 +1244,7 @@ public class PsiUtil { return resolveResult; } } - return GroovyResolveResult.EMPTY_RESULT; + return EmptyGroovyResolveResult.INSTANCE; } public static boolean isReferenceWithoutQualifier(@Nullable PsiElement element, @NotNull String name) { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ConstructorMapApplicabilityProvider.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ConstructorMapApplicabilityProvider.kt new file mode 100644 index 000000000000..9f418ff99e9b --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ConstructorMapApplicabilityProvider.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2017 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 org.jetbrains.plugins.groovy.lang.resolve + +import com.intellij.psi.* +import com.intellij.psi.util.InheritanceUtil.isInheritor +import org.jetbrains.plugins.groovy.extensions.GroovyApplicabilityProvider +import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil.ApplicabilityResult +import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil.ApplicabilityResult.applicable +import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil.ApplicabilityResult.inapplicable + + +open class ConstructorMapApplicabilityProvider : GroovyApplicabilityProvider() { + + open fun isConstructor(method: PsiMethod): Boolean { + return method.isConstructor + } + + override fun isApplicable(argumentTypes: Array, + method: PsiMethod, + substitutor: PsiSubstitutor?, + place: PsiElement?, + eraseParameterTypes: Boolean): ApplicabilityResult? { + if (!isConstructor(method)) return null + + val parameters = method.parameterList.parameters + if (parameters.isEmpty() && argumentTypes.size == 1) { + return if (isInheritor(argumentTypes[0], CommonClassNames.JAVA_UTIL_MAP)) applicable else inapplicable + } + if (parameters.size == 1 && argumentTypes.isEmpty() && isInheritor(parameters[0].type, CommonClassNames.JAVA_UTIL_MAP)) { + return inapplicable + } + return null + } +} \ No newline at end of file diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/delegatesTo/grDelegatesToUtil.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/delegatesTo/grDelegatesToUtil.kt index b58f00800a11..9c2fbb6ccfca 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/delegatesTo/grDelegatesToUtil.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/delegatesTo/grDelegatesToUtil.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -21,8 +21,8 @@ import com.intellij.psi.util.CachedValueProvider.Result import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiModificationTracker import com.intellij.util.ArrayUtil +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult.EMPTY_RESULT import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall @@ -70,5 +70,5 @@ private fun doResolveActualCall(call: GrMethodCall): GroovyResolveResult { val type = expression.type ?: return result val calls = ResolveUtil.getMethodCandidates(type, "call", expression, *getArgumentTypes(expression, false)) - return calls.singleOrNull() ?: EMPTY_RESULT + return calls.singleOrNull() ?: EmptyGroovyResolveResult } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewifyNamedArgumentProvider.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/GroovyNewifyNamedArgumentProvider.kt similarity index 65% rename from plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewifyNamedArgumentProvider.kt rename to plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/GroovyNewifyNamedArgumentProvider.kt index fe7f9dea4ab8..36bddb5d5a8e 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyNewifyNamedArgumentProvider.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/GroovyNewifyNamedArgumentProvider.kt @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.plugins.groovy.lang +package org.jetbrains.plugins.groovy.lang.resolve.newify import com.intellij.psi.PsiClass -import com.intellij.psi.PsiMethod -import com.intellij.psi.PsiMirrorElement +import org.jetbrains.plugins.groovy.lang.GroovyConstructorNamedArgumentProvider import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall class GroovyNewifyNamedArgumentProvider : GroovyConstructorNamedArgumentProvider() { - internal override fun getCorrespondingClasses(call: GrCall, resolveResult: GroovyResolveResult): List { - val prototype = (resolveResult.element as? PsiMirrorElement)?.prototype as? PsiMethod ?: return emptyList() - if (!prototype.isConstructor) return emptyList() - return prototype.containingClass?.let { listOf(it) } ?: emptyList() + override fun getCorrespondingClasses(call: GrCall, resolveResult: GroovyResolveResult): List { + val resolved = (resolveResult.element as? NewifyMemberContributor.NewifiedConstructor) ?: return emptyList() + return resolved.containingClass?.let { listOf(it) } ?: emptyList() } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyConstructorApplicabilityProvider.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyConstructorApplicabilityProvider.kt new file mode 100644 index 000000000000..853ff7dffcd7 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyConstructorApplicabilityProvider.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2017 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 org.jetbrains.plugins.groovy.lang.resolve.newify + +import com.intellij.psi.PsiMethod +import org.jetbrains.plugins.groovy.lang.resolve.ConstructorMapApplicabilityProvider + +class NewifyConstructorApplicabilityProvider : ConstructorMapApplicabilityProvider() { + override fun isConstructor(method: PsiMethod): Boolean { + return method is NewifyMemberContributor.NewifiedConstructor + } +} \ No newline at end of file diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/NewifyMemberContributor.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyMemberContributor.kt similarity index 52% rename from plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/NewifyMemberContributor.kt rename to plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyMemberContributor.kt index 871a65d737fc..2e0d648ea065 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/NewifyMemberContributor.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/newify/NewifyMemberContributor.kt @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.plugins.groovy.lang.resolve.noncode +package org.jetbrains.plugins.groovy.lang.resolve.newify -import com.intellij.lang.java.JavaLanguage import com.intellij.psi.* -import com.intellij.psi.impl.light.LightMethod -import com.intellij.psi.impl.light.LightModifierList +import com.intellij.psi.impl.light.LightMethodBuilder import com.intellij.psi.scope.PsiScopeProcessor import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil import org.jetbrains.plugins.groovy.lang.psi.util.getParents import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil internal val newifyAnnotationFqn = "groovy.lang.Newify" +internal val newifyOriginInfo = "by @Newify" class NewifyMemberContributor : NonCodeMembersContributor() { override fun processDynamicElements(qualifierType: PsiType, @@ -38,14 +38,16 @@ class NewifyMemberContributor : NonCodeMembersContributor() { val qualifier = getQualifier(place) for (annotation in listNewifyAnnotations(place)) { val newifiedClasses = GrAnnotationUtil.getClassArrayValue(annotation, "value", true) - qualifier ?: newifiedClasses.flatMap { it.constructors.asList() }.forEach { - ResolveUtil.processElement(processor, NewifiedConstructor(it, "by @Newify", it.name, true), state) + + qualifier ?: newifiedClasses.flatMap { buildConstructors(it, it.name, true) }.forEach { + ResolveUtil.processElement(processor, it, state) } val createNewMethods = GrAnnotationUtil.inferBooleanAttributeNotNull(annotation, "auto") val type = (qualifier as? GrReferenceExpression)?.resolve() as? PsiClass if (type != null && createNewMethods) { - type.constructors.forEach { - ResolveUtil.processElement(processor, NewifiedConstructor(it, "by @Newify", "new", false), state) + val constructors = buildConstructors(type, "new", false) + constructors.forEach { + ResolveUtil.processElement(processor, it, state) } } } @@ -67,36 +69,50 @@ class NewifyMemberContributor : NonCodeMembersContributor() { return (elem as? GrReferenceExpression)?.qualifierExpression } - class NewifiedConstructor(val myPrototype: PsiMethod, - val myOriginInfo: String, - val newName: String, - val asConstructor: Boolean) - : LightMethod(myPrototype.manager, myPrototype, myPrototype.containingClass!!), OriginInfoAwareElement, PsiMirrorElement { - override fun getPrototype(): PsiElement { - return myPrototype + fun buildConstructors(clazz: PsiClass, newName: String?, asConstructor: Boolean): List { + newName ?: return emptyList() + val constructors = clazz.constructors + if (constructors.isNotEmpty()) { + return constructors.mapNotNull { buildNewifiedConstructor(it, newName, asConstructor) } } - - val myModifierList: LightModifierList = LightModifierList(myPrototype.manager, JavaLanguage.INSTANCE, PsiModifier.STATIC) - - - override fun getName(): String { - return newName - } - - override fun getOriginInfo(): String { - return myOriginInfo - } - - override fun hasModifierProperty(name: String): Boolean { - return myModifierList.hasModifierProperty(name) - } - - override fun getModifierList(): PsiModifierList { - return myModifierList - } - - override fun isConstructor(): Boolean { - return asConstructor + else { + return listOf(buildNewifiedConstructor(clazz, newName, asConstructor)) } } + + fun buildNewifiedConstructor(myPrototype: PsiMethod, newName: String, asConstructor: Boolean): NewifiedConstructor? { + val builder = NewifiedConstructor(myPrototype.manager, newName) + val psiClass = myPrototype.containingClass ?: return null + builder.containingClass = psiClass + builder.setMethodReturnType(TypesUtil.createType(psiClass)) + builder.navigationElement = myPrototype + builder.isConstructor = asConstructor + myPrototype.parameterList.parameters.forEach { + builder.addParameter(it) + } + myPrototype.throwsList.referencedTypes.forEach { + builder.addException(it) + } + myPrototype.typeParameters.forEach { + builder.addTypeParameter(it) + } + return builder + } + + fun buildNewifiedConstructor(myPrototype: PsiClass, newName: String, asConstructor: Boolean): NewifiedConstructor { + val builder = NewifiedConstructor(myPrototype.manager, newName) + builder.containingClass = myPrototype + builder.setMethodReturnType(TypesUtil.createType(myPrototype)) + builder.navigationElement = myPrototype + builder.isConstructor = asConstructor + return builder + } + + class NewifiedConstructor(val myManager: PsiManager, val newName: String) : LightMethodBuilder(myManager, newName) { + init { + addModifier(PsiModifier.STATIC) + originInfo = newifyOriginInfo + } + + } } \ No newline at end of file diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 16716de5643e..e07e767658ec 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -46,6 +46,7 @@ + @@ -99,7 +100,7 @@ - + @@ -179,10 +180,12 @@ - + + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/GenerationUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/GenerationUtil.java index 4d60ca185632..d0ade448d594 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/GenerationUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/GenerationUtil.java @@ -33,6 +33,7 @@ import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.formatter.GrControlStatement; @@ -153,7 +154,7 @@ public class GenerationUtil { @NotNull GrNamedArgument[] namedArgs, @NotNull GrClosableBlock[] closureArgs, @NotNull GroovyPsiElement psiContext) { - GroovyResolveResult call = GroovyResolveResult.EMPTY_RESULT; + GroovyResolveResult call = EmptyGroovyResolveResult.INSTANCE; final PsiType type; if (caller == null) { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NewifySupportTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NewifySupportTest.groovy index 91e8f17474ff..db42914c109d 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NewifySupportTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NewifySupportTest.groovy @@ -38,6 +38,11 @@ public class A { public A(){} public A(String name){} } + +public class A2 { + String name; + int age; +} ''') } @@ -84,6 +89,57 @@ class B { """ } + void testAutoNewifyImplicitConstructor() { + testHighlighting """ +@Newify +class B { + def a = A2.new() +} +""" + testHighlighting """ +@Newify +class B { + def a = A2.new("B") +} +""" + + testHighlighting """ +@Newify +class B { + def a = A2.new(name :"bar") +} +""" + + testHighlighting """ +class B { + @Newify(B) + def b = B() +} +""" + + testHighlighting """ +class B { + @Newify + def a = B.new() +} +""" + + testHighlighting """ +class B2 { + String str + @Newify + def a = B2.new(str: "B2") +} +""" + + testHighlighting """ +class B { + @Newify(value = A2, auto = false) + def a (){ return A2.new()} +} +""" + } + void testNewifyByClass() { testHighlighting """ @Newify([A, Integer]) @@ -155,6 +211,19 @@ class B { } } + void testNewifyLookupImplicitConstructor() { + fixture.configureByText 'a.groovy', """ +@Newify +class B { + def b = B. +} +""" + fixture.completeBasic() + fixture.lookupElementStrings.with { + assert contains("new") + } + } + void testNewifyAutoMapLookup() { testHighlighting """ @Newify(A) diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy index 7d2c92162a13..285408914c63 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy @@ -885,6 +885,12 @@ class Aa { String name; public Aa(){} } +''') + + fixture.addClass(''' +class Cc { + String name; +} ''') def resolved = configureByText(""" @@ -894,6 +900,16 @@ class B { } """).resolve() assertInstanceOf(resolved, PsiMethod) + assertEquals "Aa", (resolved as PsiMethod).returnType.canonicalText + + resolved = configureByText(""" +@Newify(Cc) +class B { + def a = Cc() +} +""").resolve() + assertInstanceOf(resolved, PsiMethod) + assertEquals "Cc", (resolved as PsiMethod).returnType.canonicalText resolved = configureByText(""" @Newify(Aa) @@ -903,6 +919,7 @@ class B { """).resolve() assertInstanceOf(resolved, PsiMethod) + assertEquals "Aa", (resolved as PsiMethod).returnType.canonicalText resolved = configureByText(""" class B { @@ -912,6 +929,7 @@ class B { """).resolve() assertInstanceOf(resolved, PsiMethod) + assertEquals "Aa", (resolved as PsiMethod).returnType.canonicalText resolved = configureByText(""" class B { @@ -921,6 +939,7 @@ class B { """).resolve() assertInstanceOf(resolved, PsiMethod) + assertEquals "Aa", (resolved as PsiMethod).returnType.canonicalText resolved = configureByText(""" class B { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java index 67979119747b..482cf5c2d1f6 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java @@ -26,7 +26,6 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgProjectSettings; @@ -34,7 +33,6 @@ import org.zmlx.hg4idea.repo.HgRepository; import org.zmlx.hg4idea.repo.HgRepositoryManager; import org.zmlx.hg4idea.util.HgUtil; -import javax.swing.*; import java.util.List; import java.util.Objects; @@ -79,12 +77,6 @@ public class HgBranchPopup extends DvcsBranchPopup { super(currentRepository, repositoryManager, hgMultiRootBranchConfig, vcsSettings, preselectActionCondition, DIMENSION_SERVICE_KEY); } - protected void setCurrentBranchInfo() { - String branchText = "Current branch : "; - //always display heavy branch name for additional info // - myPopup.setAdText(branchText + myCurrentRepository.getCurrentBranch(), SwingConstants.CENTER); - } - @Override protected void fillWithCommonRepositoryActions(@NotNull DefaultActionGroup popupGroup, @NotNull AbstractRepositoryManager repositoryManager) { @@ -99,16 +91,26 @@ public class HgBranchPopup extends DvcsBranchPopup { List branchActions = myMultiRootBranchConfig.getLocalBranchNames().stream() .map(b -> createLocalBranchActions(allRepositories, b, false)) - .filter(Objects::nonNull).collect(toList()); - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(branchActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(branchActions), SHOW_ALL_BRANCHES_KEY, true); + .filter(Objects::nonNull).sorted(FAVORITE_BRANCH_COMPARATOR).collect(toList()); + int topShownBranches = getNumOfTopShownBranches(branchActions); + String commonBranch = myMultiRootBranchConfig.getCommonName(HgRepository::getCurrentBranch); + if (commonBranch != null) { + branchActions.add(0, new HgBranchPopupActions.CurrentBranch(myProject, allRepositories, commonBranch)); + topShownBranches++; + } + wrapWithMoreActionIfNeeded(myProject, popupGroup, branchActions, topShownBranches, SHOW_ALL_BRANCHES_KEY, true); popupGroup.addSeparator("Common Bookmarks"); List bookmarkActions = ((HgMultiRootBranchConfig)myMultiRootBranchConfig).getBookmarkNames().stream() .map(bm -> createLocalBranchActions(allRepositories, bm, true)) - .filter(Objects::nonNull).collect(toList()); - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(bookmarkActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(bookmarkActions), SHOW_ALL_BOOKMARKS_KEY, true); + .filter(Objects::nonNull).sorted(FAVORITE_BRANCH_COMPARATOR).collect(toList()); + int topShownBookmarks = getNumOfTopShownBranches(bookmarkActions); + String commonBookmark = myMultiRootBranchConfig.getCommonName(HgRepository::getCurrentBookmark); + if (commonBookmark != null) { + bookmarkActions.add(0, new HgBranchPopupActions.CurrentActiveBookmark(myProject, allRepositories, commonBookmark)); + topShownBookmarks++; + } + wrapWithMoreActionIfNeeded(myProject, popupGroup, bookmarkActions, topShownBookmarks, SHOW_ALL_BOOKMARKS_KEY, true); } @Nullable @@ -124,9 +126,9 @@ public class HgBranchPopup extends DvcsBranchPopup { protected DefaultActionGroup createRepositoriesActions() { DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); popupGroup.addSeparator("Repositories"); - List rootActions = DvcsUtil.sortRepositories(myRepositoryManager.getRepositories()).stream() - .map(repo -> new RootAction<>(repo, new HgBranchPopupActions(repo.getProject(), repo).createActions(), - HgUtil.getDisplayableBranchOrBookmarkText(repo))).collect(toList()); + List rootActions = DvcsUtil.sortRepositories(myRepositoryManager.getRepositories()).stream().map( + repo -> new RootAction<>(repo, new HgBranchPopupActions(repo.getProject(), repo).createActions(), + isBranchesDiverged() ? HgUtil.getDisplayableBranchOrBookmarkText(repo) : null)).collect(toList()); wrapWithMoreActionIfNeeded(myProject, popupGroup, rootActions, rootActions.size() > MAX_NUM ? DEFAULT_NUM : MAX_NUM, SHOW_ALL_REPOSITORIES); return popupGroup; diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java index b80579503082..1adb7c3478e3 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java @@ -18,6 +18,7 @@ package org.zmlx.hg4idea.branch; import com.intellij.dvcs.DvcsUtil; import com.intellij.dvcs.repo.Repository; import com.intellij.dvcs.ui.NewBranchAction; +import com.intellij.dvcs.ui.PopupElementWithAdditionalInfo; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnAction; @@ -34,7 +35,6 @@ import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.ui.CommitChangeListDialog; import com.intellij.util.ArrayUtil; -import com.intellij.util.PlatformIcons; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.Hash; import com.intellij.vcs.log.impl.HashImpl; @@ -87,13 +87,19 @@ public class HgBranchPopupActions { } popupGroup.addSeparator("Bookmarks" + repoInfo); + String currentBookmark = myRepository.getCurrentBookmark(); List bookmarkActions = getSortedNamesWithoutHashes(myRepository.getBookmarks()).stream() + .filter(bm -> !bm.equals(currentBookmark)) .map(bm -> new BookmarkActions(myProject, Collections.singletonList(myRepository), bm)) + .sorted(FAVORITE_BRANCH_COMPARATOR) .collect(toList()); - // if there are only a few local favorites -> show all; for remotes it's better to show only favorites; - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(bookmarkActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(bookmarkActions), firstLevelGroup ? HgBranchPopup.SHOW_ALL_BOOKMARKS_KEY : null, - firstLevelGroup); + int topShownBookmarks = getNumOfTopShownBranches(bookmarkActions); + if (currentBookmark != null) { + bookmarkActions.add(0, new CurrentActiveBookmark(myProject, Collections.singletonList(myRepository), currentBookmark)); + topShownBookmarks++; + } + wrapWithMoreActionIfNeeded(myProject, popupGroup, bookmarkActions, topShownBookmarks, + firstLevelGroup ? HgBranchPopup.SHOW_ALL_BOOKMARKS_KEY : null, firstLevelGroup); //only opened branches have to be shown popupGroup.addSeparator("Branches" + repoInfo); @@ -102,10 +108,11 @@ public class HgBranchPopupActions { .sorted() .filter(b -> !b.equals(myRepository.getCurrentBranch())) .map(b -> new BranchActions(myProject, Collections.singletonList(myRepository), b)) + .sorted(FAVORITE_BRANCH_COMPARATOR) .collect(toList()); - wrapWithMoreActionIfNeeded(myProject, popupGroup, ContainerUtil.sorted(branchActions, FAVORITE_BRANCH_COMPARATOR), - getNumOfTopShownBranches(branchActions), firstLevelGroup ? HgBranchPopup.SHOW_ALL_BRANCHES_KEY : null, - firstLevelGroup); + branchActions.add(0, new CurrentBranch(myProject, Collections.singletonList(myRepository), myRepository.getCurrentBranch())); + wrapWithMoreActionIfNeeded(myProject, popupGroup, branchActions, getNumOfTopShownBranches(branchActions) + 1, + firstLevelGroup ? HgBranchPopup.SHOW_ALL_BRANCHES_KEY : null, firstLevelGroup); return popupGroup; } @@ -279,6 +286,24 @@ public class HgBranchPopupActions { super(project, repositories, branchName, HgBranchType.BRANCH); } } + + public static class CurrentBranch extends BranchActions implements PopupElementWithAdditionalInfo { + public CurrentBranch(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { + super(project, repositories, branchName); + } + + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return AnAction.EMPTY_ARRAY; + } + + @Nullable + @Override + public String getPrefixInfo() { + return "current"; + } + } /** * Actions available for bookmarks. @@ -287,9 +312,6 @@ public class HgBranchPopupActions { BookmarkActions(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { super(project, repositories, branchName, HgBranchType.BOOKMARK); - if (myRepositories.size() == 1 && branchName.equals(myRepositories.get(0).getCurrentBookmark())) { - getTemplatePresentation().setIcon(PlatformIcons.CHECK_ICON); - } } @NotNull @@ -314,4 +336,23 @@ public class HgBranchPopupActions { } } } + + public static class CurrentActiveBookmark extends BookmarkActions implements PopupElementWithAdditionalInfo { + + public CurrentActiveBookmark(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { + super(project, repositories, branchName); + } + + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return new AnAction[]{new BookmarkActions.DeleteBookmarkAction(myProject, myRepositories, myBranchName)}; + } + + @Nullable + @Override + public String getPrefixInfo() { + return "active"; + } + } } diff --git a/python/IntelliLang-python/src/resources/pyInjections.xml b/python/IntelliLang-python/src/resources/pyInjections.xml index 0c04cf7018d4..b9c1d06c4c7d 100644 --- a/python/IntelliLang-python/src/resources/pyInjections.xml +++ b/python/IntelliLang-python/src/resources/pyInjections.xml @@ -80,5 +80,11 @@ + + + + + + diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudySettings.java b/python/educational-core/src/com/jetbrains/edu/learning/StudySettings.java index 03351ab16fe1..637ed5d4088d 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudySettings.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudySettings.java @@ -17,6 +17,7 @@ public class StudySettings implements PersistentStateComponent { private StepicUser myUser; public long LAST_TIME_CHECKED = 0; private boolean myEnableTestingFromSamples = false; + public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx(); public StudySettings() { } @@ -55,6 +56,14 @@ public class StudySettings implements PersistentStateComponent { updateStepicUserWidget(); } + public boolean shouldUseJavaFx() { + return myShouldUseJavaFx; + } + + public void setShouldUseJavaFx(boolean shouldUseJavaFx) { + this.myShouldUseJavaFx = shouldUseJavaFx; + } + private static void updateStepicUserWidget() { StudyStepicUserWidget widget = StudyUtils.getStepicWidget(); if (widget != null) { diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java index 9fe00e9759d3..33692d12095f 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -50,7 +50,6 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public final Map> myUserTests = new HashMap<>(); - public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx(); private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT; private boolean myTurnEditingMode = false; @@ -232,14 +231,6 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb return ServiceManager.getService(project, StudyTaskManager.class); } - public boolean shouldUseJavaFx() { - return myShouldUseJavaFx; - } - - public void setShouldUseJavaFx(boolean shouldUseJavaFx) { - this.myShouldUseJavaFx = shouldUseJavaFx; - } - public StudyToolWindow.StudyToolWindowMode getToolWindowMode() { return myToolWindowMode; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudySwitchTaskPanelAction.kt b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudySwitchTaskPanelAction.kt index 449503989fd5..2b15f0bbd192 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudySwitchTaskPanelAction.kt +++ b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudySwitchTaskPanelAction.kt @@ -1,11 +1,11 @@ package com.jetbrains.edu.learning.actions +import com.intellij.openapi.actionSystem.ActionPlaces.ACTION_SEARCH import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.project.Project import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.DialogWrapper -import com.jetbrains.edu.learning.StudyTaskManager +import com.jetbrains.edu.learning.StudySettings import com.jetbrains.edu.learning.StudyUtils import javax.swing.DefaultComboBoxModel import javax.swing.JComponent @@ -16,45 +16,22 @@ class StudySwitchTaskPanelAction: AnAction() { override fun actionPerformed(e: AnActionEvent?) { val project = e?.project - if (project != null) { - if (createDialog(project).showAndGet()) { - StudyUtils.initToolWindows(project) - } + val result = createDialog().showAndGet() + if (result && project != null) { + StudyUtils.initToolWindows(project) } } - fun createDialog(project: Project): DialogWrapper { - return MyDialog(project, false) + fun createDialog(): DialogWrapper { + return MyDialog(false) } - - - class MyDialog: DialogWrapper { + + class MyDialog(canBeParent: Boolean) : DialogWrapper(null, canBeParent) { val JAVAFX_ITEM = "JavaFX" val SWING_ITEM = "Swing" - private val myProject: Project - private val myComboBox: ComboBox + private val myComboBox: ComboBox = ComboBox() - - constructor(project: Project, canBeParent: Boolean) : super(project, canBeParent) { - myProject = project - myComboBox = ComboBox() - val comboBoxModel = DefaultComboBoxModel() - - if (StudyUtils.hasJavaFx()) { - comboBoxModel.addElement(JAVAFX_ITEM) - } - comboBoxModel.addElement(SWING_ITEM) - - comboBoxModel.selectedItem = - if (StudyUtils.hasJavaFx() && StudyTaskManager.getInstance(project).shouldUseJavaFx()) JAVAFX_ITEM else SWING_ITEM - myComboBox.model = comboBoxModel - title = "Switch Task Description Panel" - myComboBox.setMinimumAndPreferredWidth(250) - init() - } - - - override fun createCenterPanel(): JComponent? { + override fun createCenterPanel(): JComponent? { return myComboBox } @@ -68,17 +45,27 @@ class StudySwitchTaskPanelAction: AnAction() { override fun doOKAction() { super.doOKAction() - StudyTaskManager.getInstance(myProject).setShouldUseJavaFx(myComboBox.selectedItem == JAVAFX_ITEM) + StudySettings.getInstance().setShouldUseJavaFx(myComboBox.selectedItem == JAVAFX_ITEM) + } + + init { + val comboBoxModel = DefaultComboBoxModel() + if (StudyUtils.hasJavaFx()) { + comboBoxModel.addElement(JAVAFX_ITEM) + } + comboBoxModel.addElement(SWING_ITEM) + comboBoxModel.selectedItem = + if (StudyUtils.hasJavaFx() && StudySettings.getInstance().shouldUseJavaFx()) JAVAFX_ITEM else SWING_ITEM + myComboBox.model = comboBoxModel + title = "Switch Task Description Panel" + myComboBox.setMinimumAndPreferredWidth(250) + init() } } override fun update(e: AnActionEvent?) { + val place = e?.place val project = e?.project - if (project != null && StudyUtils.isStudyProject(project)) { - e?.presentation?.isEnabled = true - } - else { - e?.presentation?.isEnabled = false - } + e?.presentation?.isEnabled = project != null && StudyUtils.isStudyProject(project) || ACTION_SEARCH == place } } \ No newline at end of file diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java b/python/educational-core/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java index b93c6774ced0..5dd63e2c5943 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java @@ -58,7 +58,8 @@ public class CCCreateAnswerPlaceholderPanel { myHintsPanel.setBorder(BorderFactory.createLineBorder(JBColor.border())); ((GridLayoutManager)myHintsPanel.getLayout()).setHGap(1); - myHintTextArea.setFont(myPlaceholderTextArea.getFont()); + myHintTextArea.setFont(UIUtil.getLabelFont()); + myPlaceholderTextArea.setFont(UIUtil.getLabelFont()); myHintTextArea.addFocusListener(createFocusListenerToSetDefaultHintText()); actionsPanel.add(createHintToolbarComponent(), BorderLayout.WEST); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyHint.kt b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyHint.kt index aa213f8f16a3..01e2be6a8065 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyHint.kt +++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyHint.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.actionSystem.DefaultActionGroup import com.intellij.openapi.actionSystem.Presentation import com.intellij.openapi.project.Project import com.jetbrains.edu.coursecreator.actions.CCEditHintAction +import com.jetbrains.edu.learning.StudySettings import com.jetbrains.edu.learning.StudyTaskManager import com.jetbrains.edu.learning.StudyUtils import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder @@ -25,8 +26,7 @@ open class StudyHint(private val myPlaceholder: AnswerPlaceholder?, protected var isEditingMode = false init { - val taskManager = StudyTaskManager.getInstance(myProject) - if (StudyUtils.hasJavaFx() && taskManager.shouldUseJavaFx()) { + if (StudyUtils.hasJavaFx() && StudySettings.getInstance().shouldUseJavaFx()) { studyToolWindow = StudyJavaFxToolWindow() } else { @@ -39,7 +39,7 @@ open class StudyHint(private val myPlaceholder: AnswerPlaceholder?, studyToolWindow.setActionToolbar(DefaultActionGroup()) } - val course = taskManager.course + val course = StudyTaskManager.getInstance(myProject).course if (course != null) { val group = DefaultActionGroup() val hints = myPlaceholder?.hints diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java index 00fb4d35891f..a10d70e423a2 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java @@ -7,6 +7,7 @@ import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManager; +import com.jetbrains.edu.learning.StudySettings; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.Course; @@ -20,11 +21,10 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware { @Override public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) { toolWindow.setIcon(EducationalCoreIcons.TaskDescription); - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - final Course course = taskManager.getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course != null) { final StudyToolWindow studyToolWindow; - if (StudyUtils.hasJavaFx() && taskManager.shouldUseJavaFx()) { + if (StudyUtils.hasJavaFx() && StudySettings.getInstance().shouldUseJavaFx()) { studyToolWindow = new StudyJavaFxToolWindow(); } else { diff --git a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java index b9eed80db896..95be2c1581d0 100644 --- a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java +++ b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java @@ -12,6 +12,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.EditorTestUtil; import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; @@ -187,7 +188,7 @@ public abstract class CCTestCase extends LightPlatformCodeInsightFixtureTestCase public Pair> getPlaceholders(String name, boolean useLength, boolean removeMarkers) { try { - String text = FileUtil.loadFile(new File(getBasePath(), name)); + String text = StringUtil.convertLineSeparators(FileUtil.loadFile(new File(getBasePath(), name))); Document tempDocument = EditorFactory.getInstance().createDocument(text); if (removeMarkers) { EditorTestUtil.extractCaretAndSelectionMarkers(tempDocument); diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index f3d9d030fd2d..2e896252409b 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -413,26 +413,14 @@ def change_attr_expression(thread_id, frame_id, attr, expression, dbg, value=SEN MAXIMUM_ARRAY_SIZE = 100 -def table_like_struct_to_xml(array, name, roffset, coffset, rows, cols, format): - _, type_name, _ = get_type(array) - if type_name == 'ndarray': - array, metaxml, r, c, f = array_to_meta_xml(array, name, format) - xml = metaxml - format = '%' + f - if rows == -1 and cols == -1: - rows = r - cols = c - xml += array_to_xml(array, roffset, coffset, rows, cols, format) - elif type_name == 'DataFrame': - xml = dataframe_to_xml(array, name, roffset, coffset, rows, cols, format) - else: - raise VariableError("Do not know how to convert type %s to table" % (type_name)) - return "%s" % xml +def array_to_xml(array, name, roffset, coffset, rows, cols, format): + array, xml, r, c, f = array_to_meta_xml(array, name, format) + format = '%' + f + if rows == -1 and cols == -1: + rows = r + cols = c - -def array_to_xml(array, roffset, coffset, rows, cols, format): - xml = "" rows = min(rows, MAXIMUM_ARRAY_SIZE) cols = min(cols, MAXIMUM_ARRAY_SIZE) @@ -453,26 +441,19 @@ def array_to_xml(array, roffset, coffset, rows, cols, format): array = array[roffset:] rows = min(rows, len(array)) - xml += "" % (rows, cols) - for row in range(rows): - xml += "" % to_string(row) - for col in range(cols): - value = array - if rows == 1 or cols == 1: - if rows == 1 and cols == 1: - value = array[0] - else: - if rows == 1: - dim = col - else: - dim = row - value = array[dim] - if "ndarray" in str(type(value)): - value = value[0] + def get_value(row, col): + value = array + if rows == 1 or cols == 1: + if rows == 1 and cols == 1: + value = array[0] else: - value = array[row][col] - value = format % value - xml += var_to_xml(value, '') + value = array[(col if rows == 1 else row)] + if "ndarray" in str(type(value)): + value = value[0] + else: + value = array[row][col] + return value + xml += array_data_to_xml(rows, cols, lambda r: (get_value(r, c) for c in range(cols))) return xml @@ -533,9 +514,7 @@ def array_to_meta_xml(array, name, format): bounds = (0, 0) if type in "biufc": bounds = (array.min(), array.max()) - xml = '' % \ - (slice, rows, cols, format, type, bounds[1], bounds[0]) - return array, xml, rows, cols, format + return array, slice_to_xml(slice, rows, cols, format, type, bounds), rows, cols, format def array_default_format(type): @@ -547,6 +526,10 @@ def array_default_format(type): return 's' +def get_label(label): + return str(label) if not isinstance(label, tuple) else '/'.join(map(str, label)) + + def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): """ :type df: pandas.core.frame.DataFrame @@ -559,60 +542,80 @@ def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): """ + dim = len(df.axes) num_rows = df.shape[0] - num_cols = df.shape[1] - if (num_rows, num_cols) != df.shape: - df = df.iloc[0:num_rows, 0: num_cols] - slice = '.iloc[0:%s, 0:%s]' % (num_rows, num_cols) - else: - slice = '' - slice = name + slice - xml = '\n' % \ - (slice, num_rows, num_cols) + num_cols = df.shape[1] if dim > 1 else 1 + xml = slice_to_xml(name, num_rows, num_cols, "", "", (0, 0)) if (rows, cols) == (-1, -1): rows, cols = num_rows, num_cols rows = min(rows, MAXIMUM_ARRAY_SIZE) - cols = min(min(cols, MAXIMUM_ARRAY_SIZE), num_cols) + cols = min(cols, MAXIMUM_ARRAY_SIZE, num_cols) # need to precompute column bounds here before slicing! col_bounds = [None] * cols - for col in range(cols): - dtype = df.dtypes.iloc[coffset + col].kind - if dtype in "biufc": - cvalues = df.iloc[:, coffset + col] - bounds = (cvalues.min(), cvalues.max()) - else: - bounds = (0, 0) - col_bounds[col] = bounds - - df = df.iloc[roffset: roffset + rows, coffset: coffset + cols] - rows, cols = df.shape - - - xml += "\n" % (rows, cols) - format = format.replace('%', '') - col_formats = [] - - get_label = lambda label: str(label) if not isinstance(label, tuple) else '/'.join(map(str, label)) - - for col in range(cols): - dtype = df.dtypes.iloc[col].kind - fmt = format if (dtype == 'f' and format) else array_default_format(dtype) - col_formats.append('%' + fmt) - bounds = col_bounds[col] - - xml += '\n' % \ - (str(col), get_label(df.axes[1].values[col]), dtype, fmt, bounds[1], bounds[0]) - for row, label in enumerate(iter(df.axes[0])): - xml += "\n" % \ - (str(row), get_label(label)) - xml += "\n" - xml += "\n" % (rows, cols) - for row in range(rows): - xml += "\n" % str(row) + dtypes = [None] * cols + if dim > 1: for col in range(cols): - value = df.iat[row, col] - value = col_formats[col] % value + dtype = df.dtypes.iloc[coffset + col].kind + dtypes[col] = dtype + if dtype in "biufc": + cvalues = df.iloc[:, coffset + col] + bounds = (cvalues.min(), cvalues.max()) + else: + bounds = (0, 0) + col_bounds[col] = bounds + else: + dtype = df.dtype.kind + dtypes[0] = dtype + col_bounds[0] = (df.min(), df.max()) if dtype in "biufc" else (0, 0) + + df = df.iloc[roffset: roffset + rows, coffset: coffset + cols] if dim > 1 else df.iloc[roffset: roffset + rows] + rows = df.shape[0] + cols = df.shape[1] if dim > 1 else 1 + format = format.replace('%', '') + + def col_to_format(c): + return format if dtypes[c] == 'f' and format else array_default_format(dtypes[c]) + + xml += header_data_to_xml(rows, cols, dtypes, col_bounds, col_to_format, df, dim) + xml += array_data_to_xml(rows, cols, lambda r: (("%" + col_to_format(c)) % (df.iat[r, c] if dim > 1 else df.iat[r]) + for c in range(cols))) + return xml + + +def array_data_to_xml(rows, cols, get_row): + xml = "\n" % (rows, cols) + for row in range(rows): + xml += "\n" % to_string(row) + for value in get_row(row): xml += var_to_xml(value, '') return xml + + +def slice_to_xml(slice, rows, cols, format, type, bounds): + return '' % \ + (slice, rows, cols, format, type, bounds[1], bounds[0]) + + +def header_data_to_xml(rows, cols, dtypes, col_bounds, col_to_format, df, dim): + xml = "\n" % (rows, cols) + for col in range(cols): + col_label = get_label(df.axes[1].values[col]) if dim > 1 else str(col) + bounds = col_bounds[col] + xml += '\n' % \ + (str(col), col_label, dtypes[col], col_to_format(col), bounds[1], bounds[0]) + for row in range(rows): + xml += "\n" % (str(row), get_label(df.axes[0].values[row])) + xml += "\n" + return xml + +TYPE_TO_XML_CONVERTERS = {"ndarray": array_to_xml, "DataFrame": dataframe_to_xml, "Series": dataframe_to_xml} + + +def table_like_struct_to_xml(array, name, roffset, coffset, rows, cols, format): + _, type_name, _ = get_type(array) + if type_name in TYPE_TO_XML_CONVERTERS: + return "%s" % TYPE_TO_XML_CONVERTERS[type_name](array, name, roffset, coffset, rows, cols, format) + else: + raise VariableError("type %s not supported" % type_name) diff --git a/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java b/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java index 70f636351f3a..f35ce48bee2d 100644 --- a/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java +++ b/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java @@ -165,6 +165,10 @@ public class PyTokenTypes { public static final TokenSet WHITESPACE_OR_LINEBREAK = TokenSet.create(SPACE, TAB, FORMFEED, LINE_BREAK); public static final TokenSet OPEN_BRACES = TokenSet.create(LBRACKET, LBRACE, LPAR); public static final TokenSet CLOSE_BRACES = TokenSet.create(RBRACKET, RBRACE, RPAR); + + public static final TokenSet NUMERIC_LITERALS = TokenSet.create(FLOAT_LITERAL, INTEGER_LITERAL, IMAGINARY_LITERAL); + public static final TokenSet BOOL_LITERALS = TokenSet.create(TRUE_KEYWORD, FALSE_KEYWORD); + public static final TokenSet SCALAR_LITERALS = TokenSet.orSet(STRING_NODES, BOOL_LITERALS, NUMERIC_LITERALS, TokenSet.create(NONE_KEYWORD)); public static final TokenSet AUG_ASSIGN_OPERATIONS = TokenSet.create(PLUSEQ, MINUSEQ, MULTEQ, ATEQ, DIVEQ, PERCEQ, EXPEQ, GTGTEQ, LTLTEQ, ANDEQ, OREQ, XOREQ, FLOORDIVEQ); diff --git a/python/psi-api/src/com/jetbrains/python/psi/impl/PyOverridingTypeProvider.java b/python/psi-api/src/com/jetbrains/python/psi/impl/PyOverridingTypeProvider.java new file mode 100644 index 000000000000..ca59095b042c --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/psi/impl/PyOverridingTypeProvider.java @@ -0,0 +1,19 @@ +/* + * Copyright 2000-2017 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.jetbrains.python.psi.impl; + +public interface PyOverridingTypeProvider extends PyTypeProvider { +} diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java index 09444ad6d618..546da2bd248d 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java @@ -1,6 +1,7 @@ package com.jetbrains.python.debugger; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; import com.intellij.icons.AllIcons; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; @@ -10,6 +11,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -17,6 +19,9 @@ import java.util.regex.Pattern; // todo: null modifier for modify modules, class objects etc. public class PyDebugValue extends XNamedValue { private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.pydev.PyDebugValue"); + private static final String DATA_FRAME = "DataFrame"; + private static final String SERIES = "Series"; + private static final Map EVALUATOR_POSTFIXES = ImmutableMap.of("ndarray", "Array", DATA_FRAME, DATA_FRAME, SERIES, SERIES); public static final int MAX_VALUE = 256; public static final String RETURN_VALUES_PREFIX = "__pydevd_ret_val_dict"; @@ -204,23 +209,16 @@ public class PyDebugValue extends XNamedValue { node.setPresentation(getValueIcon(), myType, value, myContainer); } - private boolean isDataFrame() { - return "DataFrame".equals(myType); - } - - private boolean isNdarray() { - return "ndarray".equals(myType); - } - private void setFullValueEvaluator(XValueNode node, String value) { String treeName = getFullTreeName(); - if (!isDataFrame() && !isNdarray()) { + String postfix = EVALUATOR_POSTFIXES.get(myType); + if (postfix == null) { if (value.length() >= MAX_VALUE) { node.setFullValueEvaluator(new PyFullValueEvaluator(myFrameAccessor, treeName)); } return; } - String linkText = "...View as " + (isDataFrame() ? "DataFrame" : "Array"); + String linkText = "...View as " + postfix; node.setFullValueEvaluator(new PyNumericContainerValueEvaluator(linkText, myFrameAccessor, treeName)); } diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml index 1fcd94f60f08..eb4d7a1905f1 100644 --- a/python/src/META-INF/python-core-common.xml +++ b/python/src/META-INF/python-core-common.xml @@ -680,6 +680,7 @@ + diff --git a/python/src/com/jetbrains/numpy/codeInsight/NumpyResolveRater.java b/python/src/com/jetbrains/numpy/codeInsight/NumpyResolveRater.java index 1ab734ab0dde..6bf899f43c9c 100644 --- a/python/src/com/jetbrains/numpy/codeInsight/NumpyResolveRater.java +++ b/python/src/com/jetbrains/numpy/codeInsight/NumpyResolveRater.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -29,7 +29,7 @@ public class NumpyResolveRater extends PyResolveResultRaterBase { @Override public int getMemberRate(PsiElement member, PyType type, TypeEvalContext context) { if (member instanceof PsiNamedElement) { - final PyType ndArray = PyTypeParser.getTypeByName(member, NumpyDocStringTypeProvider.NDARRAY); + final PyType ndArray = PyTypeParser.getTypeByName(member, NumpyDocStringTypeProvider.NDARRAY, context); if (ndArray != null && PyTypeChecker.match(ndArray, type, context) && PyNames.isRightOperatorName(((PsiNamedElement)member).getName())) { return 100; diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index eea21e952154..f8f65fa4930b 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -150,6 +150,7 @@ QFIX.NAME.add.specifier=Add format specifier character QFIX.NAME.add.exception.base=Add Exception base class QFIX.NAME.change.signature=Change signature +QFIX.change.signature.of=Change signature of {0} QFIX.NAME.remove.argument=Remove argument diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java index 2eea3ed647ff..44489ad8a501 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java @@ -76,7 +76,7 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { if (args.length == 1 && args[0] instanceof PyReferenceExpression) { final PyReferenceExpression target = (PyReferenceExpression)args[0]; - pushAssertion(target, myPositive, context -> PyTypeParser.getTypeByName(target, "collections." + PyNames.CALLABLE)); + pushAssertion(target, myPositive, context -> PyTypeParser.getTypeByName(target, "collections." + PyNames.CALLABLE, context)); } } } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java index 2835e373a58c..818790416894 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -39,17 +39,25 @@ import java.util.Set; * @author yole */ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType { + + @NotNull + private final PsiElement myDeclaration; + + @NotNull private final String myName; - // 2 - namedtuple call itself - // 1 - return type of namedtuple call, aka namedtuple class - // 0 - namedtuple instance - private final int myDefinitionLevel; - private final PsiElement myDeclaration; + @NotNull private final List myFields; - public PyNamedTupleType(PyClass tupleClass, PsiElement declaration, String name, List fields, int definitionLevel) { - super(tupleClass, definitionLevel > 0); + @NotNull + private final DefinitionLevel myDefinitionLevel; + + public PyNamedTupleType(@NotNull PyClass tupleClass, + @NotNull PsiElement declaration, + @NotNull String name, + @NotNull List fields, + @NotNull DefinitionLevel definitionLevel) { + super(tupleClass, definitionLevel != DefinitionLevel.INSTANCE); myDeclaration = declaration; myFields = fields; myName = name; @@ -76,7 +84,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType @Override public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - List result = new ArrayList<>(); + final List result = new ArrayList<>(); Collections.addAll(result, super.getCompletionVariants(completionPrefix, location, context)); for (String field : myFields) { result.add(LookupElementBuilder.create(field)); @@ -84,6 +92,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType return ArrayUtil.toObjectArray(result); } + @NotNull @Override public String getName() { return myName; @@ -97,22 +106,30 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType @Nullable @Override public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) { - if (myDefinitionLevel > 0) { - return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, myDefinitionLevel - 1); + if (myDefinitionLevel == DefinitionLevel.AS_SUPERCLASS) { + return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, DefinitionLevel.NEW_TYPE); } + else if (myDefinitionLevel == DefinitionLevel.NEW_TYPE) { + return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, DefinitionLevel.INSTANCE); + } + return null; } @NotNull @Override public PyClassType toInstance() { - return myDefinitionLevel == 1 ? new PyNamedTupleType(myClass, myDeclaration, myName, myFields, 0) : this; + return myDefinitionLevel == DefinitionLevel.NEW_TYPE + ? new PyNamedTupleType(myClass, myDeclaration, myName, myFields, DefinitionLevel.INSTANCE) + : this; } @NotNull @Override public PyClassLikeType toClass() { - return myDefinitionLevel == 0 ? this : new PyNamedTupleType(myClass, myDeclaration, myName, myFields, 1); + return myDefinitionLevel == DefinitionLevel.INSTANCE + ? this + : new PyNamedTupleType(myClass, myDeclaration, myName, myFields, DefinitionLevel.NEW_TYPE); } @Override @@ -137,4 +154,11 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType public List getElementNames() { return Collections.unmodifiableList(myFields); } + + public enum DefinitionLevel { + + AS_SUPERCLASS, + NEW_TYPE, + INSTANCE + } } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibOverridingTypeProvider.kt b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibOverridingTypeProvider.kt new file mode 100644 index 000000000000..90b7b71ad7b6 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibOverridingTypeProvider.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2017 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.jetbrains.python.codeInsight.stdlib + +import com.intellij.psi.PsiElement +import com.jetbrains.python.PyNames +import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider +import com.jetbrains.python.psi.PyFunction +import com.jetbrains.python.psi.impl.PyOverridingTypeProvider +import com.jetbrains.python.psi.types.PyType +import com.jetbrains.python.psi.types.PyTypeProviderBase +import com.jetbrains.python.psi.types.TypeEvalContext + +class PyStdlibOverridingTypeProvider : PyTypeProviderBase(), PyOverridingTypeProvider { + + override fun getReferenceType(referenceTarget: PsiElement, context: TypeEvalContext, anchor: PsiElement?): PyType? { + return if (isTypingNamedTupleInit(referenceTarget)) PyStdlibTypeProvider.getNamedTupleType(referenceTarget, context, anchor) else null + } + + private fun isTypingNamedTupleInit(referenceTarget: PsiElement): Boolean { + return referenceTarget is PyFunction && + PyNames.INIT == referenceTarget.name && + PyTypingTypeProvider.NAMEDTUPLE == referenceTarget.containingClass?.qualifiedName + } +} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index 14fb0f2f313d..a11685feaa9d 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -151,7 +151,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { } } else if ("enum.EnumMeta.__members__".equals(name)) { - return PyTypeParser.getTypeByName(referenceTarget, "dict[str, unknown]"); + return PyTypeParser.getTypeByName(referenceTarget, "dict[str, unknown]", context); } } return null; @@ -163,7 +163,8 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { final String qname = function.getQualifiedName(); if (qname != null) { if (OPEN_FUNCTIONS.contains(qname) && callSite instanceof PyCallExpression) { - return getOpenFunctionType(qname, PyCallExpressionHelper.mapArguments(callSite, function, context).getMappedParameters(), callSite); + final PyCallExpressionHelper.ArgumentMappingResults mapping = PyCallExpressionHelper.mapArguments(callSite, function, context); + return getOpenFunctionType(qname, mapping.getMappedParameters(), callSite, context); } else if ("tuple.__init__".equals(qname) && callSite instanceof PyCallExpression) { return getTupleInitializationType((PyCallExpression)callSite, context); @@ -279,21 +280,21 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getNamedTupleType(@NotNull PsiElement referenceTarget, - @NotNull TypeEvalContext context, - @Nullable PsiElement anchor) { + static PyType getNamedTupleType(@NotNull PsiElement referenceTarget, + @NotNull TypeEvalContext context, + @Nullable PsiElement anchor) { if (referenceTarget instanceof PyTargetExpression) { final PyTargetExpression target = (PyTargetExpression)referenceTarget; final PyTargetExpressionStub stub = target.getStub(); if (stub != null) { - return getNamedTupleTypeFromStub(target, stub.getCustomStub(PyNamedTupleStub.class), 1); + return getNamedTupleTypeFromStub(target, stub.getCustomStub(PyNamedTupleStub.class), PyNamedTupleType.DefinitionLevel.NEW_TYPE); } else { - return getNamedTupleTypeFromAST(target, context, 1); + return getNamedTupleTypeFromAST(target, context, PyNamedTupleType.DefinitionLevel.NEW_TYPE); } } else if (referenceTarget instanceof PyFunction && anchor instanceof PyCallExpression) { - return getNamedTupleTypeFromAST((PyCallExpression)anchor, context, 2); + return getNamedTupleTypeFromAST((PyCallExpression)anchor, context, PyNamedTupleType.DefinitionLevel.AS_SUPERCLASS); } return null; } @@ -301,7 +302,8 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @NotNull private static Ref getOpenFunctionType(@NotNull String callQName, @NotNull Map arguments, - @NotNull PsiElement anchor) { + @NotNull PsiElement anchor, + @NotNull TypeEvalContext context) { String mode = "r"; for (Map.Entry entry : arguments.entrySet()) { final PyNamedParameter parameter = entry.getValue(); @@ -319,20 +321,20 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { if (LanguageLevel.forElement(anchor).isAtLeast(LanguageLevel.PYTHON30) || "io.open".equals(callQName) || "_io.open".equals(callQName)) { if (mode.contains("b")) { - return Ref.create(PyTypeParser.getTypeByName(anchor, PY3K_BINARY_FILE_TYPE)); + return Ref.create(PyTypeParser.getTypeByName(anchor, PY3K_BINARY_FILE_TYPE, context)); } else { - return Ref.create(PyTypeParser.getTypeByName(anchor, PY3K_TEXT_FILE_TYPE)); + return Ref.create(PyTypeParser.getTypeByName(anchor, PY3K_TEXT_FILE_TYPE, context)); } } - return Ref.create(PyTypeParser.getTypeByName(anchor, PY2K_FILE_TYPE)); + return Ref.create(PyTypeParser.getTypeByName(anchor, PY2K_FILE_TYPE, context)); } @Nullable private static PyType getNamedTupleTypeFromStub(@NotNull PsiElement referenceTarget, @Nullable PyNamedTupleStub stub, - int definitionLevel) { + @NotNull PyNamedTupleType.DefinitionLevel definitionLevel) { if (stub == null) { return null; } @@ -349,7 +351,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @Nullable private static PyType getNamedTupleTypeFromAST(@NotNull PyTargetExpression expression, @NotNull TypeEvalContext context, - int definitionLevel) { + @NotNull PyNamedTupleType.DefinitionLevel definitionLevel) { if (context.maySwitchToAST(expression)) { return getNamedTupleTypeFromStub(expression, PyNamedTupleStubImpl.create(expression), definitionLevel); } @@ -360,7 +362,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @Nullable private static PyType getNamedTupleTypeFromAST(@NotNull PyCallExpression expression, @NotNull TypeEvalContext context, - int definitionLevel) { + @NotNull PyNamedTupleType.DefinitionLevel definitionLevel) { if (context.maySwitchToAST(expression)) { return getNamedTupleTypeFromStub(expression, PyNamedTupleStubImpl.create(expression), definitionLevel); } diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index b27b06811b83..d08af5e4b638 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -23,7 +23,10 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiPolyVariantReference; -import com.intellij.psi.util.*; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.QualifiedName; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; @@ -55,6 +58,8 @@ import static com.jetbrains.python.psi.PyUtil.as; public class PyTypingTypeProvider extends PyTypeProviderBase { private static final Object RECURSION_KEY = new Object(); + public static final String TYPING = "typing"; + public static final String GENERATOR = "typing.Generator"; public static final String ASYNC_GENERATOR = "typing.AsyncGenerator"; public static final String COROUTINE = "typing.Coroutine"; @@ -63,6 +68,8 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { public static final String TYPE = "typing.Type"; public static final String ANY = "typing.Any"; + public static final String NAMEDTUPLE_SIMPLE = "NamedTuple"; + public static final Pattern TYPE_COMMENT_PATTERN = Pattern.compile("# *type: *(.*)"); private static final ImmutableMap COLLECTION_CLASSES = ImmutableMap.builder() @@ -479,7 +486,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (parameterizedType != null) { return Ref.create(parameterizedType); } - final PyType builtinCollection = getBuiltinCollection(resolved); + final PyType builtinCollection = getBuiltinCollection(resolved, context.getTypeContext()); if (builtinCollection != null) { return Ref.create(builtinCollection); } @@ -743,10 +750,10 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getBuiltinCollection(@NotNull PsiElement element) { + private static PyType getBuiltinCollection(@NotNull PsiElement element, @NotNull TypeEvalContext context) { final String collectionName = getQualifiedName(element); final String builtinName = COLLECTION_CLASSES.get(collectionName); - return builtinName != null ? PyTypeParser.getTypeByName(element, builtinName) : null; + return builtinName != null ? PyTypeParser.getTypeByName(element, builtinName, context) : null; } @NotNull diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java index 39c85123ceb5..353a5a72c265 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java @@ -48,7 +48,6 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorSettings; import com.intellij.openapi.editor.actionSystem.EditorAction; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.editor.actions.SplitLineAction; @@ -784,7 +783,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner { SoftWrapAction() { super(ActionsBundle.actionText("EditorToggleUseSoftWraps"), ActionsBundle.actionDescription("EditorToggleUseSoftWraps"), AllIcons.Actions.ToggleSoftWrap); - myConsoleView.getEditor().getSettings().setUseSoftWraps(isSelected); + updateEditors(); } @Override @@ -792,16 +791,15 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner { return isSelected; } + private void updateEditors() { + myConsoleView.getEditor().getSettings().setUseSoftWraps(isSelected); + myConsoleView.getConsoleEditor().getSettings().setUseSoftWraps(isSelected); + } + @Override public void setSelected(AnActionEvent e, boolean state) { isSelected = state; - EditorSettings editorSettings = getConsoleView().getEditor().getSettings(); - if (isSelected) { - editorSettings.setUseSoftWraps(true); - } - else { - editorSettings.setUseSoftWraps(false); - } + updateEditors(); myConsoleSettings.setUseSoftWraps(isSelected); } } diff --git a/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java b/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java index 41734caedd8a..335c9dc4e503 100644 --- a/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java +++ b/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -34,7 +34,7 @@ public class PyCallSignatureTypeProvider extends PyTypeProviderBase { if (name != null) { final String typeName = PySignatureCacheManager.getInstance(param.getProject()).findParameterType(func, name); if (typeName != null) { - final PyType type = PyTypeParser.getTypeByName(param, typeName); + final PyType type = PyTypeParser.getTypeByName(param, typeName, context); if (type != null) { return Ref.create(PyDynamicallyEvaluatedType.create(type)); } @@ -51,7 +51,7 @@ public class PyCallSignatureTypeProvider extends PyTypeProviderBase { if (signature != null && signature.getReturnType() != null) { final String typeName = signature.getReturnType().getTypeQualifiedName(); if (typeName != null) { - final PyType type = PyTypeParser.getTypeByName(function, typeName); + final PyType type = PyTypeParser.getTypeByName(function, typeName, context); if (type != null) { return Ref.create(PyDynamicallyEvaluatedType.create(type)); } diff --git a/python/src/com/jetbrains/python/debugger/PySignatureUtil.java b/python/src/com/jetbrains/python/debugger/PySignatureUtil.java deleted file mode 100644 index 21094b579750..000000000000 --- a/python/src/com/jetbrains/python/debugger/PySignatureUtil.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2000-2014 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.jetbrains.python.debugger; - -import com.google.common.collect.Collections2; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiElement; -import com.jetbrains.python.PyNames; -import com.jetbrains.python.psi.PyClass; -import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.types.PyClassType; -import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.psi.types.PyTypeParser; -import com.jetbrains.python.psi.types.PyUnionType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author traff - */ -public class PySignatureUtil { - private PySignatureUtil() { - } - - @Nullable - public static String getShortestImportableName(@Nullable PsiElement anchor, @NotNull String type) { - final PyType pyType = PyTypeParser.getTypeByName(anchor, type); - if (pyType instanceof PyClassType) { - PyClass c = ((PyClassType)pyType).getPyClass(); - return c.getQualifiedName(); - } - - if (pyType != null) { - return getPrintableName(pyType); - } - else { - return type; - } - } - - private static String getPrintableName(PyType type) { - if (type instanceof PyUnionType) { - return StringUtil.join(Collections2.transform(((PyUnionType)type).getMembers(), input -> getPrintableName(input)), " or "); - } - else if (type != null) { - return type.getName(); - } - else { - return PyNames.UNKNOWN_TYPE; - } - } - - @Nullable - public static String getArgumentType(@NotNull PyFunction function, @NotNull String name) { - PySignatureCacheManager cacheManager = PySignatureCacheManager.getInstance(function.getProject()); - PySignature signature = cacheManager.findSignature(function); - if (signature != null) { - return getShortestImportableName(function, signature.getArgTypeQualifiedName(name)); - } - return null; - } -} diff --git a/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java b/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java index 1eb2678b3715..7f40c9caca3c 100644 --- a/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java +++ b/python/src/com/jetbrains/python/debugger/containerview/DataViewStrategy.java @@ -15,12 +15,13 @@ */ package com.jetbrains.python.debugger.containerview; -import com.intellij.util.containers.ContainerUtil; +import com.google.common.collect.ImmutableSet; import com.jetbrains.python.debugger.ArrayChunk; import com.jetbrains.python.debugger.PyDebugValue; import com.jetbrains.python.debugger.array.ArrayViewStrategy; import com.jetbrains.python.debugger.array.AsyncArrayTableModel; import com.jetbrains.python.debugger.dataframe.DataFrameViewStrategy; +import com.jetbrains.python.debugger.dataframe.SeriesViewStrategy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,7 +30,7 @@ import java.util.Set; public abstract class DataViewStrategy { private static class StrategyHolder { - private static final Set STRATEGIES = ContainerUtil.newHashSet(new ArrayViewStrategy(), new DataFrameViewStrategy()); + private static final Set STRATEGIES = ImmutableSet.of(new ArrayViewStrategy(), new DataFrameViewStrategy(), new SeriesViewStrategy()); } public abstract AsyncArrayTableModel createTableModel(int rowCount, int columnCount, @NotNull PyDataViewerPanel panel, @NotNull PyDebugValue debugValue); @@ -41,6 +42,10 @@ public abstract class DataViewStrategy { @NotNull public abstract String getTypeName(); + public boolean showColumnHeader() { + return true; + } + /** * @return null if no strategy for this type */ diff --git a/python/src/com/jetbrains/python/debugger/containerview/PyDataViewerPanel.java b/python/src/com/jetbrains/python/debugger/containerview/PyDataViewerPanel.java index a61f40f4141c..3d4c187e1961 100644 --- a/python/src/com/jetbrains/python/debugger/containerview/PyDataViewerPanel.java +++ b/python/src/com/jetbrains/python/debugger/containerview/PyDataViewerPanel.java @@ -205,6 +205,7 @@ public class PyDataViewerPanel extends JPanel { if (myTable.getColumnCount() > 0) { myTable.setDefaultRenderer(myTable.getColumnClass(0), cellRenderer); } + myTable.setShowColumns(strategy.showColumnHeader()); }); } diff --git a/python/src/com/jetbrains/python/debugger/dataframe/SeriesViewStrategy.java b/python/src/com/jetbrains/python/debugger/dataframe/SeriesViewStrategy.java new file mode 100644 index 000000000000..1775ef163883 --- /dev/null +++ b/python/src/com/jetbrains/python/debugger/dataframe/SeriesViewStrategy.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2017 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.jetbrains.python.debugger.dataframe; + +import org.jetbrains.annotations.NotNull; + +public class SeriesViewStrategy extends DataFrameViewStrategy { + @NotNull + @Override + public String getTypeName() { + return "Series"; + } + + @Override + public boolean showColumnHeader() { + return false; + } +} diff --git a/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java b/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java index e3c2fae4e871..952e80035be6 100644 --- a/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java @@ -163,7 +163,7 @@ public class PyDocumentationBuilder { private void buildFromParameter(@NotNull final TypeEvalContext context, @Nullable final PsiElement outerElement, @NotNull final PsiElement elementDefinition) { myBody.addItem(combUp("Parameter " + PyUtil.getReadableRepr(elementDefinition, false))); - final boolean typeFromDocstringAdded = addTypeAndDescriptionFromDocstring((PyNamedParameter)elementDefinition); + final boolean typeFromDocstringAdded = addTypeAndDescriptionFromDocstring((PyNamedParameter)elementDefinition, context); if (outerElement instanceof PyExpression) { final PyType type = context.getType((PyExpression)outerElement); if (type != null) { @@ -409,11 +409,11 @@ public class PyDocumentationBuilder { } } - private void addPredefinedMethodDoc(PyFunction fun, String mothodName) { + private void addPredefinedMethodDoc(PyFunction fun, String methodName) { final PyClassType objectType = PyBuiltinCache.getInstance(fun).getObjectType(); // old- and new-style classes share the __xxx__ stuff if (objectType != null) { final PyClass objectClass = objectType.getPyClass(); - final PyFunction predefinedMethod = objectClass.findMethodByName(mothodName, false, null); + final PyFunction predefinedMethod = objectClass.findMethodByName(methodName, false, null); if (predefinedMethod != null) { final PyStringLiteralExpression predefinedDocstring = getEffectiveDocStringExpression(predefinedMethod); final String predefinedDoc = predefinedDocstring != null ? predefinedDocstring.getStringValue() : null; @@ -468,9 +468,10 @@ public class PyDocumentationBuilder { * Adds type and description representation from function docstring * * @param parameter parameter of a function + * @param context type evaluation context * @return true if type from docstring was added */ - private boolean addTypeAndDescriptionFromDocstring(@NotNull final PyNamedParameter parameter) { + private boolean addTypeAndDescriptionFromDocstring(@NotNull PyNamedParameter parameter, @NotNull TypeEvalContext context) { final PyFunction function = PsiTreeUtil.getParentOfType(parameter, PyFunction.class); if (function != null) { final String docString = PyPsiUtils.strValue(getEffectiveDocStringExpression(function)); @@ -480,7 +481,7 @@ public class PyDocumentationBuilder { final String description = typeAndDescr.second; if (type != null) { - final PyType pyType = PyTypeParser.getTypeByName(parameter, type); + final PyType pyType = PyTypeParser.getTypeByName(parameter, type, context); if (pyType instanceof PyClassType) { myBody.addItem(": ").addWith(new LinkWrapper(PythonDocumentationProvider.LINK_TYPE_PARAM), $(pyType.getName())); } diff --git a/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java b/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java index 2162d63865cb..a43bfa5936ae 100644 --- a/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java +++ b/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -39,7 +39,7 @@ public class PyDocStringTypeProvider extends PyTypeProviderBase { if (docString != null) { final String typeText = docString.getParamType(param.getName()); if (StringUtil.isNotEmpty(typeText)) { - final Ref typeRef = parseType(func, typeText); + final Ref typeRef = parseType(func, typeText, context); if (param.isPositionalContainer()) { return Ref.create(PyTypeUtil.toPositionalContainerType(param, typeRef.get())); @@ -63,7 +63,7 @@ public class PyDocStringTypeProvider extends PyTypeProviderBase { if (docString != null) { final String typeText = docString.getReturnType(); if (StringUtil.isNotEmpty(typeText)) { - return parseType(callable, typeText); + return parseType(callable, typeText, context); } } } @@ -71,8 +71,8 @@ public class PyDocStringTypeProvider extends PyTypeProviderBase { } @NotNull - private static Ref parseType(@NotNull PyCallable callable, String typeText) { - final PyType type = PyTypeParser.getTypeByName(callable, typeText); + private static Ref parseType(@NotNull PyCallable callable, @NotNull String typeText, @NotNull TypeEvalContext context) { + final PyType type = PyTypeParser.getTypeByName(callable, typeText, context); if (type != null) { type.assertValid("from docstring"); } diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java index d3f80dc20f00..1d5c432431a3 100644 --- a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java @@ -20,6 +20,7 @@ import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.lang.ASTNode; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.ResolveResult; @@ -28,6 +29,7 @@ import com.intellij.xml.util.XmlStringUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.inspections.quickfix.PyChangeSignatureQuickFix; import com.jetbrains.python.inspections.quickfix.PyRemoveArgumentQuickFix; import com.jetbrains.python.inspections.quickfix.PyRenameArgumentQuickFix; import com.jetbrains.python.psi.*; @@ -36,6 +38,7 @@ import com.jetbrains.python.psi.types.PyABCUtil; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.PyTypeChecker; import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.refactoring.changeSignature.PyChangeSignatureHandler; import one.util.streamex.StreamEx; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -207,7 +210,20 @@ public class PyArgumentListInspection extends PyInspection { // if there is only one mapping, we could suggest quick fixes final Set duplicateKeywords = getDuplicateKeywordArguments(node); - for (PyExpression argument : mappings.get(0).getUnmappedArguments()) { + final PyCallExpression.PyArgumentsMapping mapping = mappings.get(0); + if (!mapping.getUnmappedArguments().isEmpty() && mapping.getUnmappedParameters().isEmpty()) { + final PyCallExpression.PyMarkedCallee markedCallee = mapping.getMarkedCallee(); + if (markedCallee != null) { + final PyCallable callable = markedCallee.getCallable(); + final Project project = node.getProject(); + if (callable instanceof PyFunction && !PyChangeSignatureHandler.isNotUnderSourceRoot(project, callable.getContainingFile())) { + holder.registerProblem(node, PyBundle.message("INSP.unexpected.arg(s)"), PyChangeSignatureQuickFix.forMismatchedCall(mapping)); + } + } + } + + + for (PyExpression argument : mapping.getUnmappedArguments()) { final List quickFixes = Lists.newArrayList(new PyRemoveArgumentQuickFix()); if (argument instanceof PyKeywordArgument) { if (duplicateKeywords.contains(((PyKeywordArgument)argument).getKeyword())) { @@ -225,7 +241,7 @@ public class PyArgumentListInspection extends PyInspection { holder.registerProblem(node, addPossibleCalleesRepresentationAndWrapInHtml(PyBundle.message("INSP.unexpected.arg(s)"), mappings, context)); } } - + private static void highlightUnfilledParameters(@NotNull PyArgumentList node, @NotNull ProblemsHolder holder, @NotNull List mappings, diff --git a/python/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java b/python/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java index 13fab23dce28..58b69113132f 100644 --- a/python/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -20,19 +20,18 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.PyNames; import com.jetbrains.python.debugger.PySignature; import com.jetbrains.python.debugger.PySignatureCacheManager; -import com.jetbrains.python.debugger.PySignatureUtil; import com.jetbrains.python.documentation.docstrings.DocStringUtil; import com.jetbrains.python.documentation.docstrings.PlainDocString; import com.jetbrains.python.psi.PyElementGenerator; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyStringLiteralExpression; import com.jetbrains.python.psi.StructuredDocString; -import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.psi.types.PyTypeChecker; -import com.jetbrains.python.psi.types.PyTypeParser; +import com.jetbrains.python.psi.types.*; import com.jetbrains.python.toolbox.Substring; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -94,7 +93,7 @@ public class PyDocstringTypesInspection extends PyInspection { if (type != null) { String dynamicType = signature.getArgTypeQualifiedName(param); if (dynamicType != null) { - String dynamicTypeShortName = PySignatureUtil.getShortestImportableName(function, dynamicType); + String dynamicTypeShortName = getShortestImportableName(function, dynamicType); if (!match(function, dynamicType, type.getValue())) { registerProblem(node, "Dynamically inferred type '" + dynamicTypeShortName + @@ -109,9 +108,40 @@ public class PyDocstringTypesInspection extends PyInspection { } } + @Nullable + private String getShortestImportableName(@Nullable PsiElement anchor, @NotNull String type) { + final PyType pyType = PyTypeParser.getTypeByName(anchor, type, myTypeEvalContext); + if (pyType instanceof PyClassType) { + return ((PyClassType)pyType).getPyClass().getQualifiedName(); + } + + if (pyType != null) { + return getPrintableName(pyType); + } + else { + return type; + } + } + + @Nullable + private static String getPrintableName(@Nullable PyType type) { + if (type instanceof PyUnionType) { + return StreamEx + .of(((PyUnionType)type).getMembers()) + .map(Visitor::getPrintableName) + .joining(" or "); + } + else if (type != null) { + return type.getName(); + } + else { + return PyNames.UNKNOWN_TYPE; + } + } + private boolean match(PsiElement anchor, String dynamicTypeName, String specifiedTypeName) { - final PyType dynamicType = PyTypeParser.getTypeByName(anchor, dynamicTypeName); - final PyType specifiedType = PyTypeParser.getTypeByName(anchor, specifiedTypeName); + final PyType dynamicType = PyTypeParser.getTypeByName(anchor, dynamicTypeName, myTypeEvalContext); + final PyType specifiedType = PyTypeParser.getTypeByName(anchor, specifiedTypeName, myTypeEvalContext); return PyTypeChecker.match(specifiedType, dynamicType, myTypeEvalContext); } } diff --git a/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java b/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java index ed5a883e9e7c..ace34764820b 100644 --- a/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java @@ -61,16 +61,16 @@ public class PyInitNewSignatureInspection extends PyInspection { if (!PyNames.NEW.equals(functionName) && !PyNames.INIT.equals(functionName)) return; final PyClass cls = node.getContainingClass(); if (cls == null) return; - if (!cls.isNewStyleClass(null)) return; + if (!cls.isNewStyleClass(myTypeEvalContext)) return; final String complementaryName = PyNames.NEW.equals(functionName) ? PyNames.INIT : PyNames.NEW; - final PyFunction complementaryMethod = cls.findMethodByName(complementaryName, true, null); + final PyFunction complementaryMethod = cls.findMethodByName(complementaryName, true, myTypeEvalContext); if (complementaryMethod == null || PyUtil.isObjectClass(assertNotNull(complementaryMethod.getContainingClass()))) return; if (!PyUtil.isSignatureCompatibleTo(complementaryMethod, node, myTypeEvalContext) && !PyUtil.isSignatureCompatibleTo(node, complementaryMethod, myTypeEvalContext) && node.getContainingFile() == cls.getContainingFile()) { - registerProblem(node.getParameterList(), PyNames.NEW.equals(node.getName()) ? PyBundle.message("INSP.new.incompatible.to.init") : - PyBundle.message("INSP.init.incompatible.to.new"), - new PyChangeSignatureQuickFix(false)); + registerProblem(node.getParameterList(), PyBundle.message(PyNames.NEW.equals(node.getName()) ? "INSP.new.incompatible.to.init" + : "INSP.init.incompatible.to.new"), + PyChangeSignatureQuickFix.forMismatchingMethods(node, complementaryMethod)); } } } diff --git a/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java index 9540013c42e0..cf335472c09f 100644 --- a/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java @@ -69,7 +69,7 @@ public class PyMethodOverridingInspection extends PyInspection { final String msg = PyBundle.message("INSP.signature.mismatch", cls.getName() + "." + name + "()", baseClass != null ? baseClass.getName() : ""); - registerProblem(function.getParameterList(), msg, new PyChangeSignatureQuickFix(true)); + registerProblem(function.getParameterList(), msg, PyChangeSignatureQuickFix.forMismatchingMethods(function, baseMethod)); } } } diff --git a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java index e942a9755639..4977c1495d19 100644 --- a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -118,7 +118,7 @@ public class PyStringFormatInspection extends PyInspection { final PyType elementType = tupleType.getElementType(i); if (elementType != null) { final String typeName = myFormatSpec.get(String.valueOf(i + 1)); - final PyType type = typeName != null ? PyTypeParser.getTypeByName(problemTarget, typeName) : null; + final PyType type = typeName != null ? PyTypeParser.getTypeByName(problemTarget, typeName, myTypeEvalContext) : null; checkTypeCompatible(problemTarget, elementType, type); } } @@ -171,7 +171,7 @@ public class PyStringFormatInspection extends PyInspection { else if (PyUtil.instanceOf(rightExpression, PySequenceExpression.class, PyComprehensionElement.class)) { if (s != null) { checkTypeCompatible(problemTarget, builtinCache.getStrType(), - PyTypeParser.getTypeByName(problemTarget, s)); + PyTypeParser.getTypeByName(problemTarget, s, myTypeEvalContext)); return 1; } } @@ -184,7 +184,7 @@ public class PyStringFormatInspection extends PyInspection { if (PyTypeChecker.match(listType, type, myTypeEvalContext) || PyTypeChecker.match(stringType, type, myTypeEvalContext)) { checkTypeCompatible(problemTarget, builtinCache.getStrType(), - PyTypeParser.getTypeByName(problemTarget, s)); + PyTypeParser.getTypeByName(problemTarget, s, myTypeEvalContext)); return 1; } PySliceItem sliceItem = ((PySliceExpression)rightExpression).getSliceItem(); @@ -328,7 +328,7 @@ public class PyStringFormatInspection extends PyInspection { @NotNull final String expectedTypeName, @NotNull PsiElement problemTarget) { final PyType actual = myTypeEvalContext.getType(expression); - final PyType expected = PyTypeParser.getTypeByName(problemTarget, expectedTypeName); + final PyType expected = PyTypeParser.getTypeByName(problemTarget, expectedTypeName, myTypeEvalContext); if (actual != null) { checkTypeCompatible(problemTarget, actual, expected); } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index 1acbd0d2bcd0..0f61583d7b46 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -15,30 +15,114 @@ */ package com.jetbrains.python.inspections.quickfix; -import com.intellij.codeInspection.LocalQuickFix; -import com.intellij.codeInspection.ProblemDescriptor; +import com.google.common.collect.Iterators; +import com.google.common.collect.PeekingIterator; +import com.intellij.codeInspection.LocalQuickFixOnPsiElement; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.openapi.util.Conditions; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.SmartPointerManager; +import com.intellij.psi.SmartPsiElementPointer; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.HashSet; +import com.intellij.xml.util.XmlStringUtil; import com.jetbrains.python.PyBundle; -import com.jetbrains.python.PyNames; -import com.jetbrains.python.psi.PyClass; -import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.search.PySuperMethodsSearch; +import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.PyCallExpression.PyArgumentsMapping; +import com.jetbrains.python.psi.types.PyClassType; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.PyUnionType; import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.refactoring.NameSuggesterUtil; +import com.jetbrains.python.refactoring.PyRefactoringUtil; import com.jetbrains.python.refactoring.changeSignature.PyChangeSignatureDialog; import com.jetbrains.python.refactoring.changeSignature.PyMethodDescriptor; import com.jetbrains.python.refactoring.changeSignature.PyParameterInfo; -import org.jetbrains.annotations.NonNls; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.List; +import java.util.*; -public class PyChangeSignatureQuickFix implements LocalQuickFix { +import static com.jetbrains.python.psi.PyUtil.as; - private final boolean myOverridenMethod; +public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement { - public PyChangeSignatureQuickFix(boolean overriddenMethod) { - myOverridenMethod = overriddenMethod; + public static final Key CHANGE_SIGNATURE_ORIGINAL_CALL = Key.create("CHANGE_SIGNATURE_ORIGINAL_CALL"); + + @NotNull + public static PyChangeSignatureQuickFix forMismatchedCall(@NotNull PyArgumentsMapping mapping) { + assert mapping.getMarkedCallee() != null; + final PyFunction function = as(mapping.getMarkedCallee().getCallable(), PyFunction.class); + assert function != null; + final PyCallExpression callExpression = mapping.getCallExpression(); + int positionalParamAnchor = -1; + final PyParameter[] parameters = function.getParameterList().getParameters(); + for (PyParameter parameter : parameters) { + final PyNamedParameter namedParam = parameter.getAsNamed(); + final boolean isVararg = namedParam != null && (namedParam.isPositionalContainer() || namedParam.isKeywordContainer()); + if (parameter instanceof PySingleStarParameter || parameter.hasDefaultValue() || isVararg) { + break; + } + positionalParamAnchor++; + } + final List> newParameters = new ArrayList<>(); + final TypeEvalContext context = TypeEvalContext.userInitiated(function.getProject(), callExpression.getContainingFile()); + final Set usedParamNames = new HashSet<>(); + for (PyExpression arg : mapping.getUnmappedArguments()) { + if (arg instanceof PyKeywordArgument) { + final PyExpression value = ((PyKeywordArgument)arg).getValueExpression(); + final String valueText = value != null ? value.getText() : ""; + newParameters.add(Pair.create(parameters.length - 1, + new PyParameterInfo(-1, ((PyKeywordArgument)arg).getKeyword(), valueText, true))); + } + else { + final String paramName = generateParameterName(arg, function, usedParamNames, context); + newParameters.add(Pair.create(positionalParamAnchor, new PyParameterInfo(-1, paramName, arg.getText(), false))); + usedParamNames.add(paramName); + } + } + return new PyChangeSignatureQuickFix(function, newParameters, mapping.getCallExpression()); + } + + @NotNull + public static PyChangeSignatureQuickFix forMismatchingMethods(@NotNull PyFunction function, @NotNull PyFunction complementary) { + final int paramLength = function.getParameterList().getParameters().length; + final int complementaryParamLength = complementary.getParameterList().getParameters().length; + final List> extraParams; + if (complementaryParamLength > paramLength) { + extraParams = Collections.singletonList(Pair.create(paramLength - 1, new PyParameterInfo(-1, "**kwargs", "", false))); + } + else { + extraParams = Collections.emptyList(); + } + return new PyChangeSignatureQuickFix(function, extraParams, null); + } + + private final List> myExtraParameters; + private final SmartPsiElementPointer myOriginalCallExpression; + + + /** + * @param extraParameters new parameters anchored by indexes of the existing parameters they should be inserted after + * (-1 in case they should precede the first parameter) + */ + private PyChangeSignatureQuickFix(@NotNull PyFunction function, + @NotNull List> extraParameters, + @Nullable PyCallExpression expression) { + super(function); + myExtraParameters = ContainerUtil.sorted(extraParameters, Comparator.comparingInt(p -> p.getFirst())); + if (expression != null) { + myOriginalCallExpression = SmartPointerManager.getInstance(function.getProject()).createSmartPsiElementPointer(expression); + } + else { + myOriginalCallExpression = null; + } } @NotNull @@ -46,31 +130,114 @@ public class PyChangeSignatureQuickFix implements LocalQuickFix { return PyBundle.message("QFIX.NAME.change.signature"); } - public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) { - final PyFunction function = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PyFunction.class); - if (function == null) return; - final PyClass cls = function.getContainingClass(); - assert cls != null; - final String functionName = function.getName(); - final String complementaryName = PyNames.NEW.equals(functionName) ? PyNames.INIT : PyNames.NEW; - final TypeEvalContext context = TypeEvalContext.userInitiated(project, descriptor.getEndElement().getContainingFile()); - final PyFunction complementaryMethod = myOverridenMethod ? (PyFunction)PySuperMethodsSearch.search(function, context).findFirst() - : cls.findMethodByName(complementaryName, true, null); + @NotNull + @Override + public String getText() { + final PyFunction function = getFunction(); + if (function == null) { + return getFamilyName(); + } + final String params = StringUtil.join(createMethodDescriptor(function).getParameters(), info -> { + return info.getOldIndex() == -1 ? "" + info.getName() + "" : info.getName(); + }, ", "); - assert complementaryMethod != null; - final PyMethodDescriptor methodDescriptor = new PyMethodDescriptor(function) { + final String message = PyBundle.message("QFIX.change.signature.of", StringUtil.notNullize(function.getName()) + "(" + params + ")"); + return XmlStringUtil.wrapInHtml(message); + } + + @Nullable + private PyFunction getFunction() { + return (PyFunction)getStartElement(); + } + + @Override + public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { + final PyFunction function = getFunction(); + final PyMethodDescriptor descriptor = createMethodDescriptor(function); + + final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, descriptor) { + // Similar to JavaChangeSignatureDialog.createAndPreselectNew() @Override - public List getParameters() { - final List parameterInfos = super.getParameters(); - final int paramLength = function.getParameterList().getParameters().length; - final int complementaryParamLength = complementaryMethod.getParameterList().getParameters().length; - if (complementaryParamLength > paramLength) - parameterInfos.add(new PyParameterInfo(-1, "**kwargs", "", false)); - return parameterInfos; + protected int getSelectedIdx() { + return (int)StreamEx.of(getParameters()).indexOf(info -> info.getOldIndex() < 0).orElse(super.getSelectedIdx()); } }; - final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, methodDescriptor); - dialog.show(); + + final PyCallExpression originalCall = myOriginalCallExpression.getElement(); + try { + if (originalCall != null) { + originalCall.putUserData(CHANGE_SIGNATURE_ORIGINAL_CALL, true); + } + if (ApplicationManager.getApplication().isUnitTestMode()) { + dialog.createRefactoringProcessor().run(); + } + else { + dialog.show(); + } + } + finally { + if (originalCall != null) { + originalCall.putUserData(CHANGE_SIGNATURE_ORIGINAL_CALL, null); + } + } + } + + @NotNull + private static String generateParameterName(@NotNull PyExpression argumentValue, + @NotNull PyFunction function, + @NotNull Set usedParameterNames, + @NotNull TypeEvalContext context) { + final Collection suggestions = new LinkedHashSet<>(); + final PyCallExpression callExpr = as(argumentValue, PyCallExpression.class); + final PyElement referenceElem = as(callExpr != null ? callExpr.getCallee() : argumentValue, PyReferenceExpression.class); + if (referenceElem != null) { + suggestions.addAll(NameSuggesterUtil.generateNames(referenceElem.getText())); + } + if (suggestions.isEmpty()) { + PyType type = context.getType(argumentValue); + if (type instanceof PyUnionType) { + type = ContainerUtil.find(((PyUnionType)type).getMembers(), Conditions.instanceOf(PyClassType.class)); + } + final String typeName = type != null && type.getName() != null ? type.getName() : "object"; + suggestions.addAll(NameSuggesterUtil.generateNamesByType(typeName)); + } + final String shortestName = Collections.min(suggestions, Comparator.comparingInt(String::length)); + + String result = shortestName; + int counter = 1; + while (!PyRefactoringUtil.isValidNewName(result, function.getStatementList()) || usedParameterNames.contains(result)) { + result = shortestName + counter; + counter++; + } + return result; + } + + @NotNull + private PyMethodDescriptor createMethodDescriptor(final PyFunction function) { + return new PyMethodDescriptor(function) { + @Override + public List getParameters() { + final List result = new ArrayList<>(); + final List originalParams = super.getParameters(); + final PeekingIterator> extra = Iterators.peekingIterator(myExtraParameters.iterator()); + while (extra.hasNext() && extra.peek().getFirst() < 0) { + result.add(extra.next().getSecond()); + } + for (int i = 0; i < originalParams.size(); i++) { + result.add(originalParams.get(i)); + while (extra.hasNext() && extra.peek().getFirst() == i) { + result.add(extra.next().getSecond()); + } + } + return result; + } + }; + } + + @Nullable + @Override + public PsiElement getElementToMakeWritable(@NotNull PsiFile currentFile) { + return getFunction(); } @Override diff --git a/python/src/com/jetbrains/python/psi/PyFileElementType.java b/python/src/com/jetbrains/python/psi/PyFileElementType.java index 11cd013273ee..e95590bbb9f3 100644 --- a/python/src/com/jetbrains/python/psi/PyFileElementType.java +++ b/python/src/com/jetbrains/python/psi/PyFileElementType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -62,7 +62,7 @@ public class PyFileElementType extends IStubFileElementType { @Override public int getStubVersion() { // Don't forget to update versions of indexes that use the updated stub-based elements - return 58; + return 59; } @Nullable diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 7e734531e9af..43ec7333f878 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -473,6 +473,11 @@ public class PyCallExpressionHelper { @Nullable private static Ref getCallTargetReturnType(@NotNull PyCallExpression call, @NotNull PsiElement target, @NotNull TypeEvalContext context) { + final PyType providedOverridingType = PyReferenceExpressionImpl.getReferenceTypeFromOverridingProviders(target, context, call); + if (providedOverridingType instanceof PyCallableType) { + return Ref.create(((PyCallableType)providedOverridingType).getCallType(context, call)); + } + PyClass cls = null; PyFunction init = null; if (target instanceof PyClass) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java index 6b363f4e636b..50964690da49 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java @@ -41,6 +41,7 @@ import com.jetbrains.python.psi.impl.references.PyReferenceImpl; import com.jetbrains.python.psi.resolve.*; import com.jetbrains.python.psi.types.*; import com.jetbrains.python.refactoring.PyDefUseUtil; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -488,6 +489,18 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere return null; } + @Nullable + public static PyType getReferenceTypeFromOverridingProviders(@NotNull PsiElement target, + @NotNull TypeEvalContext context, + @Nullable PsiElement anchor) { + return StreamEx + .of(Extensions.getExtensions(PyTypeProvider.EP_NAME)) + .select(PyOverridingTypeProvider.class) + .map(provider -> provider.getReferenceType(target, context, anchor)) + .findFirst(Objects::nonNull) + .orElse(null); + } + @Nullable public static PyType getReferenceTypeFromProviders(@NotNull PsiElement target, @NotNull TypeEvalContext context, diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyNamedTupleStubImpl.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyNamedTupleStubImpl.java index c4a3ec6b6e37..e68dd73e5da8 100644 --- a/python/src/com/jetbrains/python/psi/impl/stubs/PyNamedTupleStubImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyNamedTupleStubImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -15,17 +15,21 @@ */ package com.jetbrains.python.psi.impl.stubs; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.stubs.StubInputStream; import com.intellij.psi.stubs.StubOutputStream; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.io.StringRef; import com.jetbrains.python.PyNames; +import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.psi.resolve.PyResolveUtil; import com.jetbrains.python.psi.stubs.PyNamedTupleStub; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -70,22 +74,22 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { return null; } - final QualifiedName namedTupleQName = getNamedTupleQName(calleeReference); + final Pair calleeNameAndModule = getCalleeNameAndNTModule(calleeReference); - if (namedTupleQName != null) { + if (calleeNameAndModule != null) { final String name = resolveTupleName(expression); if (name == null) { return null; } - final List fields = resolveTupleFields(expression); + final List fields = resolveTupleFields(expression, calleeNameAndModule.getSecond()); if (fields == null) { return null; } - return new PyNamedTupleStubImpl(namedTupleQName, name, fields); + return new PyNamedTupleStubImpl(calleeNameAndModule.getFirst(), name, fields); } return null; @@ -144,14 +148,14 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { } @Nullable - private static QualifiedName getNamedTupleQName(@NotNull PyReferenceExpression referenceExpression) { - final QualifiedName name = getFullyQualifiedNamedTupleQName(referenceExpression); + private static Pair getCalleeNameAndNTModule(@NotNull PyReferenceExpression referenceExpression) { + final Pair name = getFullyQCalleeNameAndNTModule(referenceExpression); if (name != null) { return name; } - return getImportedNamedTupleQName(referenceExpression); + return getImportedCalleeNameAndNTModule(referenceExpression); } @Nullable @@ -165,6 +169,13 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { // Point = namedtuple(("Point"), ...) + // name = "Point" + // Point = NamedTuple(name, ...) + + // Point = NamedTuple("Point", ...) + + // Point = NamedTuple(("Point"), ...) + final PyExpression nameExpression = PyPsiUtils.flattenParens(callExpression.getArgument(0, PyExpression.class)); if (nameExpression instanceof PyReferenceExpression) { @@ -175,27 +186,15 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { } @Nullable - private static List resolveTupleFields(@NotNull PyCallExpression callExpression) { - // SUPPORTED CASES: - - // fields = ["x", "y"] - // Point = namedtuple(..., fields) - - // Point = namedtuple(..., "x y") - - // Point = namedtuple(..., ("x y")) - - // Point = namedtuple(..., "x, y") - - // Point = namedtuple(..., ["x", "y"]) - - final PyExpression fieldsExpression = PyPsiUtils.flattenParens(callExpression.getArgument(1, PyExpression.class)); - - if (fieldsExpression instanceof PyReferenceExpression) { - return extractFields(fullResolveLocally((PyReferenceExpression)fieldsExpression)); + private static List resolveTupleFields(@NotNull PyCallExpression callExpression, @NotNull NamedTupleModule module) { + switch (module) { + case TYPING: + return resolveTypingNTFields(callExpression); + case COLLECTIONS: + return resolveCollectionsNTFields(callExpression); + default: + return null; } - - return extractFields(fieldsExpression); } @NotNull @@ -214,7 +213,7 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { } @Nullable - private static QualifiedName getFullyQualifiedNamedTupleQName(@NotNull PyReferenceExpression referenceExpression) { + private static Pair getFullyQCalleeNameAndNTModule(@NotNull PyReferenceExpression referenceExpression) { // SUPPORTED CASES: // import collections @@ -223,14 +222,27 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { // import collections as c // Point = c.namedtuple(...) - if (PyNames.NAMEDTUPLE.equals(referenceExpression.getName())) { + // import typing + // ... = typing.NamedTuple(...) + + // import typing as t + // ... = t.NamedTuple(...) + + final String referenceName = referenceExpression.getName(); + final NamedTupleModule module = PyNames.NAMEDTUPLE.equals(referenceName) + ? NamedTupleModule.COLLECTIONS + : PyTypingTypeProvider.NAMEDTUPLE_SIMPLE.equals(referenceName) + ? NamedTupleModule.TYPING + : null; + + if (module != null) { final PyExpression qualifier = referenceExpression.getQualifier(); if (qualifier instanceof PyReferenceExpression) { final PyReferenceExpression qualifierReference = (PyReferenceExpression)qualifier; - if (!qualifierReference.isQualified() && resolvesToCollections(qualifierReference)) { - return QualifiedName.fromComponents(qualifierReference.getName(), referenceExpression.getName()); + if (!qualifierReference.isQualified() && resolvesToModule(qualifierReference, module)) { + return Pair.createNonNull(QualifiedName.fromComponents(qualifierReference.getName(), referenceName), module); } } } @@ -239,7 +251,7 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { } @Nullable - private static QualifiedName getImportedNamedTupleQName(@NotNull PyReferenceExpression referenceExpression) { + private static Pair getImportedCalleeNameAndNTModule(@NotNull PyReferenceExpression referenceExpression) { // SUPPORTED CASES: // from collections import namedtuple @@ -248,18 +260,31 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { // from collections import namedtuple as NT // Point = NT(...) + // from typing import NamedTuple + // Point = NamedTuple(...) + + // from typing import NamedTuple as NT + // Point = NT(...) + for (PsiElement element : PyResolveUtil.resolveLocally(referenceExpression)) { if (element instanceof PyImportElement) { final PyImportElement importElement = (PyImportElement)element; + final QualifiedName importedQName = importElement.getImportedQName(); - if (equals(importElement.getImportedQName(), PyNames.NAMEDTUPLE)) { + final NamedTupleModule module = equals(importedQName, PyNames.NAMEDTUPLE) + ? NamedTupleModule.COLLECTIONS + : equals(importedQName, PyTypingTypeProvider.NAMEDTUPLE_SIMPLE) + ? NamedTupleModule.TYPING + : null; + + if (module != null) { final PyStatement importStatement = importElement.getContainingImportStatement(); if (importStatement instanceof PyFromImportStatement) { final PyFromImportStatement fromImportStatement = (PyFromImportStatement)importStatement; - if (equals(fromImportStatement.getImportSourceQName(), PyNames.COLLECTIONS)) { - return QualifiedName.fromComponents(referenceExpression.getName()); + if (equals(fromImportStatement.getImportSourceQName(), module.getModuleName())) { + return Pair.createNonNull(QualifiedName.fromComponents(referenceExpression.getName()), module); } } } @@ -269,12 +294,12 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { return null; } - private static boolean resolvesToCollections(@NotNull PyReferenceExpression referenceExpression) { + private static boolean resolvesToModule(@NotNull PyReferenceExpression referenceExpression, @NotNull NamedTupleModule module) { for (PsiElement element : PyResolveUtil.resolveLocally(referenceExpression)) { if (element instanceof PyImportElement) { final PyImportElement importElement = (PyImportElement)element; - if (equals(importElement.getImportedQName(), PyNames.COLLECTIONS)) { + if (equals(importElement.getImportedQName(), module.getModuleName())) { return true; } } @@ -305,32 +330,98 @@ public class PyNamedTupleStubImpl implements PyNamedTupleStub { } @Nullable - private static List extractFields(@Nullable PyExpression expression) { - if (expression == null) { - return null; - } + private static List resolveCollectionsNTFields(@NotNull PyCallExpression callExpression) { + // SUPPORTED CASES: - final List listValue = PyUtil.strListValue(expression); + // fields = ["x", "y"] + // Point = namedtuple(..., fields) - if (listValue != null) { - return listValue; - } + // Point = namedtuple(..., "x y") - return extractFields(PyPsiUtils.strValue(expression)); + // Point = namedtuple(..., ("x y")) + + // Point = namedtuple(..., "x, y") + + // Point = namedtuple(..., ["x", "y"]) + + final PyExpression fields = PyPsiUtils.flattenParens(callExpression.getArgument(1, PyExpression.class)); + + final PyExpression resolvedFields = fields instanceof PyReferenceExpression + ? fullResolveLocally((PyReferenceExpression)fields) + : fields; + + final List listValue = PyUtil.strListValue(resolvedFields); + if (listValue != null) return listValue; + + final String resolvedFieldsValue = PyPsiUtils.strValue(resolvedFields); + if (resolvedFieldsValue == null) return null; + + return StreamEx + .of(StringUtil.tokenize(resolvedFieldsValue, ", ").iterator()) + .toList(); } @Nullable - private static List extractFields(@Nullable String fieldsString) { - if (fieldsString == null) { - return null; + private static List resolveTypingNTFields(@NotNull PyCallExpression callExpression) { + // SUPPORTED CASES: + + // fields = [("x", str), ("y", int)] + // Point = NamedTuple(..., fields) + + // Point = NamedTuple(..., [("x", str), ("y", int)]) + + // Point = NamedTuple(..., x=str, y=int) + + final PyExpression secondArgument = PyPsiUtils.flattenParens(callExpression.getArgument(1, PyExpression.class)); + + if (secondArgument instanceof PyKeywordArgument) { + final PyExpression[] arguments = callExpression.getArguments(); + return StreamEx + .of(arguments, 1, arguments.length) + .select(PyKeywordArgument.class) + .map(PyKeywordArgument::getKeyword) + .toList(); + } else { + final PyExpression resolvedFields = secondArgument instanceof PyReferenceExpression + ? fullResolveLocally((PyReferenceExpression)secondArgument) + : secondArgument; + if (!(resolvedFields instanceof PySequenceExpression)) return null; + + final List result = new ArrayList<>(); + + for (PyExpression element : ((PySequenceExpression)resolvedFields).getElements()) { + if (!(element instanceof PyParenthesizedExpression)) return null; + + final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression(); + if (!(contained instanceof PyTupleExpression)) return null; + + final PyExpression[] nameAndType = ((PyTupleExpression)contained).getElements(); + final PyExpression name = ArrayUtil.getFirstElement(nameAndType); + if (nameAndType.length != 2 || !(name instanceof PyStringLiteralExpression)) return null; + + result.add(((PyStringLiteralExpression)name).getStringValue()); + } + + return result; } + } - final List result = new ArrayList<>(); + private enum NamedTupleModule { - for (String name : StringUtil.tokenize(fieldsString, ", ")) { - result.add(name); - } + COLLECTIONS { + @Override + public String getModuleName() { + return PyNames.COLLECTIONS; + } + }, - return result; + TYPING { + @Override + public String getModuleName() { + return PyTypingTypeProvider.TYPING; + } + }; + + public abstract String getModuleName(); } } diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java index f82c0310924b..1499e771c542 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -116,25 +116,45 @@ public class PyTypeParser { * @return null either if there was an error during parsing or if extracted type is equivalent to Any or undefined */ @Nullable - public static PyType getTypeByName(@Nullable final PsiElement anchor, @NotNull String type) { + public static PyType getTypeByName(@Nullable PsiElement anchor, @NotNull String type) { + if (anchor == null) return EMPTY_RESULT.getType(); return parse(anchor, type).getType(); } /** - * @param anchor should never be null or null will be returned + * @param anchor should never be null or null will be returned + * @param context type evaluation context + * @return null either if there was an error during parsing or if extracted type is equivalent to Any or undefined + */ + @Nullable + public static PyType getTypeByName(@Nullable PsiElement anchor, @NotNull String type, @NotNull TypeEvalContext context) { + if (anchor == null) return EMPTY_RESULT.getType(); + return parse(anchor, type, context).getType(); + } + + /** + * @param anchor should never be null or {@link PyTypeParser#EMPTY_RESULT} will be returned + * @param type representation of the type to parse */ @NotNull - public static ParseResult parse(@Nullable final PsiElement anchor, @NotNull String type) { + public static ParseResult parse(@NotNull PsiElement anchor, @NotNull String type) { + return parse(anchor, type, TypeEvalContext.codeInsightFallback(anchor.getProject())); + } + + /** + * @param anchor should never be null or {@link PyTypeParser#EMPTY_RESULT} will be returned + * @param type representation of the type to parse + * @param context type evaluation context + */ + @NotNull + public static ParseResult parse(@NotNull PsiElement anchor, @NotNull String type, @NotNull TypeEvalContext context) { PyPsiUtils.assertValid(anchor); - if (anchor == null) { - return EMPTY_RESULT; - } final ForwardDeclaration typeExpr = ForwardDeclaration.create(); final FunctionalParser classType = token(IDENTIFIER).then(many(op(".").skipThen(token(IDENTIFIER)))) - .map(new MakeSimpleType(anchor)) + .map(new MakeSimpleType(anchor, context)) .cached() .named("class-type"); @@ -304,9 +324,11 @@ public class PyTypeParser { private static class MakeSimpleType implements Function, List>>, ParseResult> { @NotNull private final PsiElement myAnchor; + @NotNull private final TypeEvalContext myContext; - public MakeSimpleType(@NotNull PsiElement anchor) { + public MakeSimpleType(@NotNull PsiElement anchor, @NotNull TypeEvalContext context) { myAnchor = anchor; + myContext = context; } @Nullable @@ -331,15 +353,14 @@ public class PyTypeParser { if (file instanceof PyFile) { final PyFile pyFile = (PyFile)file; - final TypeEvalContext context = TypeEvalContext.codeInsightFallback(file.getProject()); final Map types = new HashMap<>(); final Map fullRanges = new HashMap<>(); final Map imports = new HashMap<>(); - PyType type = resolveQualifierType(tokens, pyFile, context, types, fullRanges, imports); + PyType type = resolveQualifierType(tokens, pyFile, myContext, types, fullRanges, imports); if (type != null) { - final PyResolveContext resolveContext = PyResolveContext.defaultContext().withTypeEvalContext(context); + final PyResolveContext resolveContext = PyResolveContext.defaultContext().withTypeEvalContext(myContext); final PyExpression expression = myAnchor instanceof PyExpression ? (PyExpression)myAnchor : null; for (Token token : tokens) { @@ -350,7 +371,7 @@ public class PyTypeParser { if (results != null && !results.isEmpty()) { final PsiElement resolved = results.get(0).getElement(); if (resolved instanceof PyTypedElement) { - type = context.getType((PyTypedElement)resolved); + type = myContext.getType((PyTypedElement)resolved); if (type != null && !allowResolveToType(type)) { type = null; break; diff --git a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java index 31017cda1cc6..13365921cf80 100644 --- a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -22,8 +22,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.jetbrains.python.PyElementTypes; -import com.jetbrains.python.PythonStringUtil; -import com.jetbrains.python.inspections.PyStringFormatParser; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyPsiUtils; @@ -154,7 +152,7 @@ public class PyReplaceExpressionUtil implements PyElementTypes { final PyType valueType = context.getType(formatValue); final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(oldExpression); final PyType tupleType = builtinCache.getTupleType(); - final PyType mappingType = PyTypeParser.getTypeByName(null, "collections.Mapping"); + final PyType mappingType = PyTypeParser.getTypeByName(null, "collections.Mapping", context); if (!PyTypeChecker.match(tupleType, valueType, context) || (mappingType != null && !PyTypeChecker.match(mappingType, valueType, context))) { return replaceSubstringWithSingleValueFormatting(oldExpression, textRange, prefix, suffix, formatValue, newText, substitutions); @@ -322,12 +320,12 @@ public class PyReplaceExpressionUtil implements PyElementTypes { builder.append("("); } if (!leftQuote.endsWith(prefix)) { - builder.append(prefix + rightQuote + " + "); + builder.append(prefix).append(rightQuote).append(" + "); } final int pos = builder.toString().length(); builder.append(newText); if (!rightQuote.startsWith(suffix)) { - builder.append(" + " + leftQuote + suffix); + builder.append(" + ").append(leftQuote).append(suffix); } if (hasSubstitutions) { builder.append(")"); diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java index 0d3857a0f85d..09ba82f0c219 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureDialog.java @@ -38,13 +38,15 @@ import com.intellij.refactoring.ui.VisibilityPanelBase; import com.intellij.ui.EditorTextField; import com.intellij.ui.components.JBLabel; import com.intellij.ui.treeStructure.Tree; +import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import com.intellij.util.IJSwingUtilities; import com.intellij.util.containers.HashSet; import com.intellij.util.ui.UIUtil; -import com.intellij.util.ui.table.JBListTable; +import com.intellij.util.ui.table.EditorTextFieldJBTableRowRenderer; import com.intellij.util.ui.table.JBTableRow; import com.intellij.util.ui.table.JBTableRowEditor; +import com.intellij.util.ui.table.JBTableRowRenderer; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PythonFileType; @@ -53,7 +55,7 @@ import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyParameterList; import com.jetbrains.python.refactoring.introduce.IntroduceValidator; -import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -68,10 +70,10 @@ import java.util.Set; * User : ktisha */ -public class PyChangeSignatureDialog extends ChangeSignatureDialogBase { +public class PyChangeSignatureDialog extends + ChangeSignatureDialogBase { - public PyChangeSignatureDialog(Project project, - PyMethodDescriptor method) { + public PyChangeSignatureDialog(Project project, PyMethodDescriptor method) { super(project, method, false, method.getMethod().getContext()); } @@ -87,7 +89,7 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase parameters = getParameters(); return new PyChangeSignatureProcessor(myProject, myMethod.getMethod(), getMethodName(), parameters.toArray(new PyParameterInfo[parameters.size()])); @@ -107,9 +109,7 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase parameters = myParametersTableModel.getItems(); - Set parameterNames = new HashSet<>(); + final Set parameterNames = new HashSet<>(); boolean hadPositionalContainer = false; boolean hadKeywordContainer = false; boolean hadDefaultValue = false; boolean hadSingleStar = false; boolean hadParamsAfterSingleStar = false; - LanguageLevel languageLevel = LanguageLevel.forElement(myMethod.getMethod()); + final LanguageLevel languageLevel = LanguageLevel.forElement(myMethod.getMethod()); - int parametersLength = parameters.size(); + final int parametersLength = parameters.size(); - for (int index = 0; index != parametersLength; ++index) { - PyParameterTableModelItem info = parameters.get(index); + for (int index = 0; index < parametersLength; index++) { + final PyParameterTableModelItem info = parameters.get(index); final PyParameterInfo parameter = info.parameter; final String name = parameter.getName(); final String nameWithoutStars = StringUtil.trimLeading(name, '*').trim(); @@ -148,7 +148,7 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase { - getRefactorAction().setEnabled(message == null); - getPreviewAction().setEnabled(message == null); - }); + getRefactorAction().setEnabled(message == null); + getPreviewAction().setEnabled(message == null); if (message != null) return new ValidationInfo(message); return super.doValidate(); } - @Override - public JComponent getPreferredFocusedComponent() { - return myNameField; - } - @Override protected String calculateSignature() { - @NonNls StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append(getMethodName()); builder.append("("); final List parameters = myParametersTableModel.getItems(); - for (int i = 0; i != parameters.size(); ++i) { - PyParameterTableModelItem parameterInfo = parameters.get(i); + for (int i = 0; i < parameters.size(); i++) { + final PyParameterTableModelItem parameterInfo = parameters.get(i); builder.append(parameterInfo.parameter.getName()); final String defaultValue = parameterInfo.defaultValueCodeFragment.getText(); if (!defaultValue.isEmpty() && parameterInfo.isDefaultInSignature()) { - builder.append(" = " + defaultValue); + builder.append(" = ").append(defaultValue); } - if (i != parameters.size()-1) + if (i != parameters.size() - 1) { builder.append(", "); + } } builder.append(")"); return builder.toString(); @@ -256,151 +250,164 @@ public class PyChangeSignatureDialog extends ChangeSignatureDialogBase createVisibilityControl() { - return new ComboBoxVisibilityPanel<>(new String[0]); + return new ComboBoxVisibilityPanel<>(ArrayUtil.EMPTY_STRING_ARRAY); } @Override - protected JComponent getRowPresentation(ParameterTableModelItemBase item, boolean selected, final boolean focused) { - String text = item.parameter.getName(); - final String defaultCallValue = item.defaultValueCodeFragment.getText(); - PyParameterTableModelItem pyItem = (PyParameterTableModelItem)item; - final String defaultValue = pyItem.isDefaultInSignature()? pyItem.defaultValueCodeFragment.getText() : ""; - - if (StringUtil.isNotEmpty(defaultValue)) { - text += " = " + defaultValue; - } - - String tail = ""; - if (StringUtil.isNotEmpty(defaultCallValue)) { - tail += " default value = " + defaultCallValue; - } - if (!StringUtil.isEmpty(tail)) { - text += " //" + tail; - } - return JBListTable.createEditorTextFieldPresentation(getProject(), getFileType(), " " + text, selected, focused); - } - - @Override - protected boolean isListTableViewSupported() { - return true; - } - - @Override - protected JBTableRowEditor getTableEditor(final JTable t, final ParameterTableModelItemBase item) { - return new JBTableRowEditor() { - private EditorTextField myNameEditor; - private EditorTextField myDefaultValueEditor; - private JCheckBox myDefaultInSignature; - + protected ParametersListTable createParametersListTable() { + return new ParametersListTable() { @Override - public void prepareEditor(JTable table, int row) { - setLayout(new GridLayout(1, 3)); - final JPanel parameterPanel = createParameterPanel(); - add(parameterPanel); - final JPanel defaultValuePanel = createDefaultValuePanel(); - add(defaultValuePanel); - final JPanel defaultValueCheckBox = createDefaultValueCheckBox(); - add(defaultValueCheckBox); - - final String nameText = myNameEditor.getText(); - myDefaultValueEditor.setEnabled(!nameText.startsWith("*") - && !PyNames.CANONICAL_SELF.equals(nameText)); - myDefaultInSignature.setEnabled(!nameText.startsWith("*") - && !PyNames.CANONICAL_SELF.equals(nameText)); - } - - private JPanel createDefaultValueCheckBox() { - final JPanel defaultValuePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); - - final JBLabel inSignatureLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.default.value.checkbox"), - UIUtil.ComponentStyle.SMALL); - IJSwingUtilities.adjustComponentsOnMac(inSignatureLabel, - myDefaultInSignature); - defaultValuePanel.add(inSignatureLabel, BorderLayout.WEST); - myDefaultInSignature = new JCheckBox(); - myDefaultInSignature.setSelected( - ((PyParameterTableModelItem)item).isDefaultInSignature()); - myDefaultInSignature.addItemListener(new ItemListener() { + protected JBTableRowRenderer getRowRenderer(int row) { + return new EditorTextFieldJBTableRowRenderer(getProject(), getFileType(), getDisposable()) { @Override - public void itemStateChanged(ItemEvent event) { - ((PyParameterTableModelItem)item) - .setDefaultInSignature(myDefaultInSignature.isSelected()); - } - }); - myDefaultInSignature.addChangeListener(mySignatureUpdater); - myDefaultInSignature.setEnabled(item.parameter.getOldIndex() == -1); - defaultValuePanel.add(myDefaultInSignature, BorderLayout.EAST); - return defaultValuePanel; - } + protected String getText(JTable table, int row) { + final PyParameterTableModelItem pyItem = getRowItem(row); + final StringBuilder text = new StringBuilder(pyItem.parameter.getName()); + final String defaultCallValue = pyItem.defaultValueCodeFragment.getText(); + final String defaultValue = pyItem.isDefaultInSignature() ? pyItem.defaultValueCodeFragment.getText() : ""; - private JPanel createDefaultValuePanel() { - final JPanel defaultValuePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); - final Document doc = PsiDocumentManager.getInstance(getProject()).getDocument(item.defaultValueCodeFragment); - myDefaultValueEditor = new EditorTextField(doc, getProject(), getFileType()); - final JBLabel defaultValueLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.default.value.label"), - UIUtil.ComponentStyle.SMALL); - IJSwingUtilities.adjustComponentsOnMac(defaultValueLabel, myDefaultValueEditor); - defaultValuePanel.add(defaultValueLabel); - defaultValuePanel.add(myDefaultValueEditor); - myDefaultValueEditor.setPreferredWidth(t.getWidth() / 2); - myDefaultValueEditor.addDocumentListener(mySignatureUpdater); - return defaultValuePanel; - } - - private JPanel createParameterPanel() { - final JPanel namePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); - myNameEditor = new EditorTextField(item.parameter.getName(), getProject(), getFileType()); - final JBLabel nameLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.name.label"), - UIUtil.ComponentStyle.SMALL); - IJSwingUtilities.adjustComponentsOnMac(nameLabel, myNameEditor); - namePanel.add(nameLabel); - namePanel.add(myNameEditor); - myNameEditor.setPreferredWidth(t.getWidth() / 2); - myNameEditor.addDocumentListener(new DocumentListener() { - @Override - public void documentChanged(DocumentEvent event) { - fireDocumentChanged(event, 0); - myDefaultValueEditor.setEnabled(!myNameEditor.getText().startsWith("*")); - myDefaultInSignature.setEnabled(!myNameEditor.getText().startsWith("*")); - } - }); - - myNameEditor.addDocumentListener(mySignatureUpdater); - return namePanel; - } - - @Override - public JBTableRow getValue() { - return new JBTableRow() { - @Override - public Object getValueAt(int column) { - switch (column) { - case 0: return myNameEditor.getText().trim(); - case 1: return new Pair<>(item.defaultValueCodeFragment, - ((PyParameterTableModelItem)item).isDefaultInSignature()); + if (StringUtil.isNotEmpty(defaultValue)) { + text.append(" = ").append(defaultValue); } - return null; + + if (StringUtil.isNotEmpty(defaultCallValue)) { + text.append(" // default value = ").append(defaultCallValue); + } + return text.toString(); + } + }; + } + + @NotNull + @Override + protected JBTableRowEditor getRowEditor(ParameterTableModelItemBase item) { + return new JBTableRowEditor() { + private EditorTextField myNameEditor; + private EditorTextField myDefaultValueEditor; + private JCheckBox myDefaultInSignature; + + @Override + public void prepareEditor(JTable table, int row) { + setLayout(new GridLayout(1, 3)); + final JPanel parameterPanel = createParameterPanel(); + add(parameterPanel); + final JPanel defaultValuePanel = createDefaultValuePanel(); + add(defaultValuePanel); + final JPanel defaultValueCheckBox = createDefaultValueCheckBox(); + add(defaultValueCheckBox); + + final String nameText = myNameEditor.getText(); + myDefaultValueEditor.setEnabled(!nameText.startsWith("*") && !PyNames.CANONICAL_SELF.equals(nameText)); + myDefaultInSignature.setEnabled(!nameText.startsWith("*") && !PyNames.CANONICAL_SELF.equals(nameText)); + } + + private JPanel createDefaultValueCheckBox() { + final JPanel defaultValuePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); + + final JBLabel inSignatureLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.default.value.checkbox"), + UIUtil.ComponentStyle.SMALL); + IJSwingUtilities.adjustComponentsOnMac(inSignatureLabel, + myDefaultInSignature); + defaultValuePanel.add(inSignatureLabel, BorderLayout.WEST); + myDefaultInSignature = new JCheckBox(); + myDefaultInSignature.setSelected(((PyParameterTableModelItem)item).isDefaultInSignature()); + myDefaultInSignature.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent event) { + ((PyParameterTableModelItem)item).setDefaultInSignature(myDefaultInSignature.isSelected()); + } + }); + myDefaultInSignature.addChangeListener(mySignatureUpdater); + myDefaultInSignature.setEnabled(item.parameter.getOldIndex() == -1); + defaultValuePanel.add(myDefaultInSignature, BorderLayout.EAST); + return defaultValuePanel; + } + + private JPanel createDefaultValuePanel() { + final JPanel defaultValuePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); + final Document doc = PsiDocumentManager.getInstance(getProject()).getDocument(item.defaultValueCodeFragment); + myDefaultValueEditor = new EditorTextField(doc, getProject(), getFileType()); + final JBLabel defaultValueLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.default.value.label"), + UIUtil.ComponentStyle.SMALL); + IJSwingUtilities.adjustComponentsOnMac(defaultValueLabel, myDefaultValueEditor); + defaultValuePanel.add(defaultValueLabel); + defaultValuePanel.add(myDefaultValueEditor); + myDefaultValueEditor.setPreferredWidth(getTable().getWidth() / 2); + myDefaultValueEditor.addDocumentListener(mySignatureUpdater); + return defaultValuePanel; + } + + private JPanel createParameterPanel() { + final JPanel namePanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); + myNameEditor = new EditorTextField(item.parameter.getName(), getProject(), getFileType()); + final JBLabel nameLabel = new JBLabel(PyBundle.message("refactoring.change.signature.dialog.name.label"), + UIUtil.ComponentStyle.SMALL); + IJSwingUtilities.adjustComponentsOnMac(nameLabel, myNameEditor); + namePanel.add(nameLabel); + namePanel.add(myNameEditor); + myNameEditor.setPreferredWidth(getTable().getWidth() / 2); + myNameEditor.addDocumentListener(new DocumentListener() { + @Override + public void documentChanged(DocumentEvent event) { + fireDocumentChanged(event, 0); + myDefaultValueEditor.setEnabled(!myNameEditor.getText().startsWith("*")); + myDefaultInSignature.setEnabled(!myNameEditor.getText().startsWith("*")); + } + }); + + myNameEditor.addDocumentListener(mySignatureUpdater); + return namePanel; + } + + @Override + public JBTableRow getValue() { + return new JBTableRow() { + @Override + public Object getValueAt(int column) { + switch (column) { + case 0: + return myNameEditor.getText().trim(); + case 1: + return new Pair<>(item.defaultValueCodeFragment, + ((PyParameterTableModelItem)item).isDefaultInSignature()); + } + return null; + } + }; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myNameEditor.getFocusTarget(); + } + + @Override + public JComponent[] getFocusableComponents() { + final List focusable = new ArrayList<>(); + focusable.add(myNameEditor.getFocusTarget()); + if (myDefaultValueEditor != null) { + focusable.add(myDefaultValueEditor.getFocusTarget()); + } + if (myDefaultInSignature != null) { + focusable.add(myDefaultInSignature); + } + return focusable.toArray(new JComponent[focusable.size()]); } }; } @Override - public JComponent getPreferredFocusedComponent() { - return myNameEditor.getFocusTarget(); - } - - @Override - public JComponent[] getFocusableComponents() { - final List focusable = new ArrayList<>(); - focusable.add(myNameEditor.getFocusTarget()); - if (myDefaultValueEditor != null) { - focusable.add(myDefaultValueEditor.getFocusTarget()); - } - return focusable.toArray(new JComponent[focusable.size()]); + protected boolean isRowEmpty(int row) { + return false; } }; } + @Override + protected boolean isListTableViewSupported() { + return true; + } + @Override protected boolean mayPropagateParameters() { return false; diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java index 6b67be8045df..3fac20e8f0b4 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java @@ -139,7 +139,7 @@ public class PyChangeSignatureHandler implements ChangeSignatureHandler { CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, "refactoring.renameRefactorings"); } - private static boolean isNotUnderSourceRoot(@NotNull final Project project, @Nullable final PsiFile psiFile) { + public static boolean isNotUnderSourceRoot(@NotNull final Project project, @Nullable final PsiFile psiFile) { if (psiFile == null) { return true; } diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureProcessor.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureProcessor.java index 7dc010b07233..9b19d26d9577 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureProcessor.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureProcessor.java @@ -28,10 +28,10 @@ import org.jetbrains.annotations.NotNull; public class PyChangeSignatureProcessor extends ChangeSignatureProcessorBase { - protected PyChangeSignatureProcessor(Project project, - PyFunction method, - String newName, - PyParameterInfo[] parameterInfo) { + public PyChangeSignatureProcessor(Project project, + PyFunction method, + String newName, + PyParameterInfo[] parameterInfo) { super(project, generateChangeInfo(method, newName, parameterInfo)); } diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureUsageProcessor.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureUsageProcessor.java index 2e7a96175f84..e84de500a694 100644 --- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureUsageProcessor.java +++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureUsageProcessor.java @@ -34,6 +34,7 @@ import com.intellij.util.containers.MultiMap; import com.jetbrains.python.PyNames; import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.documentation.docstrings.PyDocstringGenerator; +import com.jetbrains.python.inspections.quickfix.PyChangeSignatureQuickFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.search.PyOverridingMethodsSearch; import com.jetbrains.python.refactoring.PyRefactoringUtil; @@ -101,8 +102,13 @@ public class PyChangeSignatureUsageProcessor implements ChangeSignatureUsageProc RenameUtil.doRenameGenericNamedElement(method, changeInfo.getNewName(), usages, null); } if (element == null) return false; + if (element.getParent() instanceof PyCallExpression) { final PyCallExpression call = (PyCallExpression)element.getParent(); + // Don't modify the call that was the cause of Change Signature invocation + if (call.getUserData(PyChangeSignatureQuickFix.CHANGE_SIGNATURE_ORIGINAL_CALL) != null) { + return true; + } final PyArgumentList argumentList = call.getArgumentList(); if (argumentList != null) { final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(element.getProject()); diff --git a/python/testData/debug/test_series.py b/python/testData/debug/test_series.py new file mode 100644 index 000000000000..151c1211927d --- /dev/null +++ b/python/testData/debug/test_series.py @@ -0,0 +1,8 @@ +import pandas as pd +import numpy as np + +frame = pd.DataFrame(data=np.random.randint(0, high=10, size=(4, 2)), columns=['a', 'b'], index=pd.MultiIndex([['s', 'd'], [2, 3]], [[0, 0, 1, 1], [0, 1, 0, 1]])) + +series = frame.a + +print(series) # line 7 diff --git a/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter.py b/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter.py new file mode 100644 index 000000000000..86ecc3f8fd07 --- /dev/null +++ b/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter.py @@ -0,0 +1,4 @@ +def func(x, *args, foo=None): + pass + +func(1, 2, 3, bar='spam') \ No newline at end of file diff --git a/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter_after.py b/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter_after.py new file mode 100644 index 000000000000..2196ab7ac340 --- /dev/null +++ b/python/testData/inspections/ChangeSignatureAddKeywordOnlyParameter_after.py @@ -0,0 +1,4 @@ +def func(x, *args, foo=None, bar='spam'): + pass + +func(1, 2, 3, bar='spam') \ No newline at end of file diff --git a/python/testData/inspections/ChangeSignatureKeywordAndPositionalParameters.py b/python/testData/inspections/ChangeSignatureKeywordAndPositionalParameters.py new file mode 100644 index 000000000000..d557fb3a02f5 --- /dev/null +++ b/python/testData/inspections/ChangeSignatureKeywordAndPositionalParameters.py @@ -0,0 +1,5 @@ +def f(x, foo=1): + pass + + +f(x, 42, bar='spam') \ No newline at end of file diff --git a/python/testData/inspections/ChangeSignatureKeywordAndPositionalParameters_after.py b/python/testData/inspections/ChangeSignatureKeywordAndPositionalParameters_after.py new file mode 100644 index 000000000000..1d9c05e5cf52 --- /dev/null +++ b/python/testData/inspections/ChangeSignatureKeywordAndPositionalParameters_after.py @@ -0,0 +1,5 @@ +def f(x, foo=1, bar='spam'): + pass + + +f(x, 42, bar='spam') \ No newline at end of file diff --git a/python/testData/inspections/ChangeSignatureNewParametersNames.py b/python/testData/inspections/ChangeSignatureNewParametersNames.py new file mode 100644 index 000000000000..1477278aa0db --- /dev/null +++ b/python/testData/inspections/ChangeSignatureNewParametersNames.py @@ -0,0 +1,5 @@ +def func(i1): + i2 = 'Spam' + + +func(1, 2, 42 or 'str', get_num()) \ No newline at end of file diff --git a/python/testData/inspections/ChangeSignatureNewParametersNames_after.py b/python/testData/inspections/ChangeSignatureNewParametersNames_after.py new file mode 100644 index 000000000000..8281e37a5ed5 --- /dev/null +++ b/python/testData/inspections/ChangeSignatureNewParametersNames_after.py @@ -0,0 +1,5 @@ +def func(i1, i, i3, num): + i2 = 'Spam' + + +func(1, 2, 42 or 'str', get_num()) \ No newline at end of file diff --git a/python/testData/inspections/ChangeSignatureParametersDefaultValues.py b/python/testData/inspections/ChangeSignatureParametersDefaultValues.py new file mode 100644 index 000000000000..eceee62d0ccc --- /dev/null +++ b/python/testData/inspections/ChangeSignatureParametersDefaultValues.py @@ -0,0 +1,6 @@ +def func(): + pass + + +func(42, foo='spam') +func() diff --git a/python/testData/inspections/ChangeSignatureParametersDefaultValues_after.py b/python/testData/inspections/ChangeSignatureParametersDefaultValues_after.py new file mode 100644 index 000000000000..046a292bc48c --- /dev/null +++ b/python/testData/inspections/ChangeSignatureParametersDefaultValues_after.py @@ -0,0 +1,6 @@ +def func(i, foo='spam'): + pass + + +func(42, foo='spam') +func(42) diff --git a/python/testData/inspections/PyArgumentListInspection/badarglist.py b/python/testData/inspections/PyArgumentListInspection/badarglist.py index 8ac2755a27aa..08bf20f42b00 100644 --- a/python/testData/inspections/PyArgumentListInspection/badarglist.py +++ b/python/testData/inspections/PyArgumentListInspection/badarglist.py @@ -18,8 +18,8 @@ def f1(): pass f1() -f1(1) -f1(a = 1) +f1(1) +f1(a = 1) def f2(a): @@ -28,19 +28,19 @@ def f2(a): f2() # ok, fail f2(1) # ok, pass -f2(1, 2) # ok, fail +f2(1, 2) # ok, fail f2(a = 1) # ok, pass f2(b = 1) # ok, fail -f2(a = 1, b = 2) # ok, fail +f2(a = 1, b = 2) # ok, fail def f3(a, b): pass f3(1, 2) -f3(1, 2, 3) +f3(1, 2, 3) f3(b=2, a=1) -f3(b=1, b=2, a=1) +f3(b=1, b=2, a=1) f3(1, b=2) f3(a=1, 2) diff --git a/python/testData/inspections/PyArgumentListInspection/decorators.py b/python/testData/inspections/PyArgumentListInspection/decorators.py index 9377b5eb8148..6cb33c5d4842 100644 --- a/python/testData/inspections/PyArgumentListInspection/decorators.py +++ b/python/testData/inspections/PyArgumentListInspection/decorators.py @@ -13,7 +13,7 @@ def f7(): def f8(): pass -@deco(1, 2) # fail: extra param +@deco(1, 2) # fail: extra param def f9(): pass @@ -57,7 +57,7 @@ class Dec2: def __init__(self, p1, p2): pass -@Dec2() # fail: no p1, p2 +@Dec2() # fail: no p1, p2 def f17(): pass diff --git a/python/testData/inspections/PyArgumentListInspection/kwargsMapToNothing.py b/python/testData/inspections/PyArgumentListInspection/kwargsMapToNothing.py index 5029c3b56bd8..c285a60cf423 100644 --- a/python/testData/inspections/PyArgumentListInspection/kwargsMapToNothing.py +++ b/python/testData/inspections/PyArgumentListInspection/kwargsMapToNothing.py @@ -5,5 +5,5 @@ arg = [1, 2, 3] kwarg = {'c':3} f5(*arg, **kwarg) # ok f5(1,2, **kwarg) # ok -f5(1, 2, 3, **kwarg) # fail -f5(1, 2, 3, *arg) # fail +f5(1, 2, 3, **kwarg) # fail +f5(1, 2, 3, *arg) # fail diff --git a/python/testData/inspections/PyArgumentListInspection/py1268.py b/python/testData/inspections/PyArgumentListInspection/py1268.py index baaa9fd9e496..951df27428ee 100644 --- a/python/testData/inspections/PyArgumentListInspection/py1268.py +++ b/python/testData/inspections/PyArgumentListInspection/py1268.py @@ -3,8 +3,8 @@ def f(a, b, c): f(c=1, *(10, 20)) f(*(10, 20), c=1) -f(*(10, 20, 30), c=1) # fail: duplicate c -f(1, *(10, 20, 30)) # fail: tuple too long +f(*(10, 20, 30), c=1) # fail: duplicate c +f(1, *(10, 20, 30)) # fail: tuple too long f(1, *(10)) # fail: wrong type f(1, *(10,)) # fail: tuple too short, c not mapped @@ -24,14 +24,14 @@ f2(*(1,2), a=1, b=2, *(1,2)) # fail: a and b twice -f3(1, 2, *(3,), c=4) # fail: c twice +f3(c=3, a=1, b=2, *(1,2)) # fail: a and b twice +f3(1, 2, *(3,), c=4) # fail: c twice f3(1,2,3, *(1,2)) f3(c=3, *(1,2)) # -f3(1, c=3, *(1,2)) # fail: c twice -f3(c=3, a=1, b=2, d=(1,2)) # fail: unexpected d +f3(1, c=3, *(1,2)) # fail: c twice +f3(c=3, a=1, b=2, d=(1,2)) # fail: unexpected d f3(1, c=3, *(10,)) # ZZZ f3(1, *(10,)) f3(1, *(10,), c=20) f3(*(1,2), c=20) -f3(*(1,2), a=20) # fail: a twice +f3(*(1,2), a=20) # fail: a twice diff --git a/python/testData/inspections/PyArgumentListInspection/py3k.py b/python/testData/inspections/PyArgumentListInspection/py3k.py index 70e7820379f0..4ac87031c611 100644 --- a/python/testData/inspections/PyArgumentListInspection/py3k.py +++ b/python/testData/inspections/PyArgumentListInspection/py3k.py @@ -19,7 +19,7 @@ def a23(a, *b, c=1): pass a23(1,2,3, c=10) # pass -a23(1,2,3, c=10, a=1) # fail +a23(1,2,3, c=10, a=1) # fail a23(c=10, a=1) # pass a23(c=10, 1) # fail a23(*args, c=1) # pass diff --git a/python/testData/inspections/PyArgumentListInspection/tupleVsLiteralList.py b/python/testData/inspections/PyArgumentListInspection/tupleVsLiteralList.py index d3c41a99bd97..3936d0df1780 100644 --- a/python/testData/inspections/PyArgumentListInspection/tupleVsLiteralList.py +++ b/python/testData/inspections/PyArgumentListInspection/tupleVsLiteralList.py @@ -2,4 +2,4 @@ def f20(a, (b, c)): pass f20(1, [2, 3]) # ok -f20(1, (2, 3, 4)) # fail +f20(1, (2, 3, 4)) # fail diff --git a/python/testData/inspections/PyTypeCheckerInspection/TypingNamedTupleAsParameter.py b/python/testData/inspections/PyTypeCheckerInspection/TypingNamedTupleAsParameter.py new file mode 100644 index 000000000000..c638e5985517 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/TypingNamedTupleAsParameter.py @@ -0,0 +1,12 @@ +from typing import NamedTuple + + +nt = NamedTuple("name", [("field", str)]) + + +def foo(x: nt): + pass + + +foo(5) +foo(nt(field = "f")) \ No newline at end of file diff --git a/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/duplicate.py b/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/duplicate.py index 4716f42c8564..47b5067cc548 100644 --- a/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/duplicate.py +++ b/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/duplicate.py @@ -2,4 +2,4 @@ def foo(a, p): pass -foo(1, p=2, p=33) +foo(1, p=2, p=33) diff --git a/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/unexpected.py b/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/unexpected.py index 82f42d31e15a..2ae09ef56bce 100644 --- a/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/unexpected.py +++ b/python/testData/quickFixes/PyRemoveArgumentQuickFixTest/unexpected.py @@ -2,6 +2,6 @@ def foo(a): pass -foo(1, 23) +foo(1, 23) diff --git a/python/testData/stubs/FullyQualifiedTypingNamedTuple.py b/python/testData/stubs/FullyQualifiedTypingNamedTuple.py new file mode 100644 index 000000000000..0a0c72972354 --- /dev/null +++ b/python/testData/stubs/FullyQualifiedTypingNamedTuple.py @@ -0,0 +1,3 @@ +import typing + +nt = typing.NamedTuple("name", [("field", str)]) \ No newline at end of file diff --git a/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargs.py b/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargs.py new file mode 100644 index 000000000000..b968fb3ec634 --- /dev/null +++ b/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargs.py @@ -0,0 +1,3 @@ +import typing + +nt = typing.NamedTuple("name", field=str) \ No newline at end of file diff --git a/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargsWithAs.py b/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargsWithAs.py new file mode 100644 index 000000000000..2d9e52c41d7b --- /dev/null +++ b/python/testData/stubs/FullyQualifiedTypingNamedTupleKwargsWithAs.py @@ -0,0 +1,3 @@ +import typing as T + +nt = T.NamedTuple("name", field=str) \ No newline at end of file diff --git a/python/testData/stubs/FullyQualifiedTypingNamedTupleWithAs.py b/python/testData/stubs/FullyQualifiedTypingNamedTupleWithAs.py new file mode 100644 index 000000000000..98b805441fb2 --- /dev/null +++ b/python/testData/stubs/FullyQualifiedTypingNamedTupleWithAs.py @@ -0,0 +1,3 @@ +import typing as T + +nt = T.NamedTuple("name", [("field", str)]) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTuple.py b/python/testData/stubs/ImportedTypingNamedTuple.py new file mode 100644 index 000000000000..f123cc049321 --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTuple.py @@ -0,0 +1,3 @@ +from typing import NamedTuple + +nt = NamedTuple("name", [("field", str)]) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleFields.py b/python/testData/stubs/ImportedTypingNamedTupleFields.py new file mode 100644 index 000000000000..649fa3cf576a --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleFields.py @@ -0,0 +1,4 @@ +from typing import NamedTuple +from b import fields + +nt = NamedTuple("name", fields) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleKwargs.py b/python/testData/stubs/ImportedTypingNamedTupleKwargs.py new file mode 100644 index 000000000000..7ee5acf92e5f --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleKwargs.py @@ -0,0 +1,3 @@ +from typing import NamedTuple + +nt = NamedTuple("name", field=str) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleKwargsName.py b/python/testData/stubs/ImportedTypingNamedTupleKwargsName.py new file mode 100644 index 000000000000..de907d47e64b --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleKwargsName.py @@ -0,0 +1,4 @@ +from typing import NamedTuple +from b import name + +nt = NamedTuple(name, x=str, y=int) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleKwargsWithAs.py b/python/testData/stubs/ImportedTypingNamedTupleKwargsWithAs.py new file mode 100644 index 000000000000..43824d8ccfba --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleKwargsWithAs.py @@ -0,0 +1,3 @@ +from typing import NamedTuple as NT + +nt = NT("name", field=str) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleName.py b/python/testData/stubs/ImportedTypingNamedTupleName.py new file mode 100644 index 000000000000..e0eb71d471bc --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleName.py @@ -0,0 +1,4 @@ +from typing import NamedTuple +from b import name + +nt = NamedTuple(name, [("x", str), ("y", int)]) \ No newline at end of file diff --git a/python/testData/stubs/ImportedTypingNamedTupleWithAs.py b/python/testData/stubs/ImportedTypingNamedTupleWithAs.py new file mode 100644 index 000000000000..ef85b30618a3 --- /dev/null +++ b/python/testData/stubs/ImportedTypingNamedTupleWithAs.py @@ -0,0 +1,3 @@ +from typing import NamedTuple as NT + +nt = NT("name", [("field", str)]) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleFieldsChain.py b/python/testData/stubs/TypingNamedTupleFieldsChain.py new file mode 100644 index 000000000000..08f97f5c22e1 --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleFieldsChain.py @@ -0,0 +1,5 @@ +from typing import NamedTuple + +fields0 = [("x", str), ("y", int)] +fields = fields0 +nt = NamedTuple("name", fields) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleFieldsReference.py b/python/testData/stubs/TypingNamedTupleFieldsReference.py new file mode 100644 index 000000000000..de9e4629f175 --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleFieldsReference.py @@ -0,0 +1,4 @@ +from typing import NamedTuple + +fields = [("x", str), ("y", int)] +nt = NamedTuple("name", fields) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleKwargsNameChain.py b/python/testData/stubs/TypingNamedTupleKwargsNameChain.py new file mode 100644 index 000000000000..af123e5b88b2 --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleKwargsNameChain.py @@ -0,0 +1,5 @@ +from typing import NamedTuple + +name0 = "name" +name = name0 +nt = NamedTuple(name, x=str, y=int) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleKwargsNameReference.py b/python/testData/stubs/TypingNamedTupleKwargsNameReference.py new file mode 100644 index 000000000000..781aedeb074c --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleKwargsNameReference.py @@ -0,0 +1,4 @@ +from typing import NamedTuple + +name = "name" +nt = NamedTuple(name, x=str, y=int) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleNameChain.py b/python/testData/stubs/TypingNamedTupleNameChain.py new file mode 100644 index 000000000000..338bc79f7077 --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleNameChain.py @@ -0,0 +1,5 @@ +from typing import NamedTuple + +name0 = "name" +name = name0 +nt = NamedTuple(name, [("x", str), ("y", int)]) \ No newline at end of file diff --git a/python/testData/stubs/TypingNamedTupleNameReference.py b/python/testData/stubs/TypingNamedTupleNameReference.py new file mode 100644 index 000000000000..3437d9e5db67 --- /dev/null +++ b/python/testData/stubs/TypingNamedTupleNameReference.py @@ -0,0 +1,4 @@ +from typing import NamedTuple + +name = "name" +nt = NamedTuple(name, [("x", str), ("y", int)]) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java b/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java index 7686c210bf46..81ffcd89ccad 100644 --- a/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java +++ b/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java @@ -20,6 +20,7 @@ import com.intellij.util.Consumer; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerTestUtil; import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.Staging; import com.jetbrains.env.python.debug.PyDebuggerTask; import com.jetbrains.python.debugger.ArrayChunk; import com.jetbrains.python.debugger.PyDebugValue; @@ -75,6 +76,20 @@ public class PythonDataViewerTest extends PyEnvTestCase { }); } + @Test + @Staging + public void testSeries() throws Exception { + runPythonTest(new PyDataFrameDebuggerTask(getRelativeTestDataPath(), "test_series.py", ImmutableSet.of(7)) { + @Override + public void testing() throws Exception { + doTest("series", 4, 1, arrayChunk -> { + List labels = arrayChunk.getRowLabels(); + assertSameElements(labels, "s/2", "s/3", "d/2", "d/3"); + }); + } + }); + } + private static class PyDataFrameDebuggerTask extends PyDebuggerTask { private Set myLines; diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 05f1b21b15f9..bf03e83e183d 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -17,8 +17,10 @@ package com.jetbrains.python; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.ex.InspectionProfileImpl; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.testFramework.TestDataFile; import com.intellij.testFramework.TestDataPath; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.documentation.docstrings.DocStringFormat; import com.jetbrains.python.fixtures.PyTestCase; @@ -647,6 +649,28 @@ public class PyQuickFixTest extends PyTestCase { " \"((/(?P.+))?))\")"); } + // PY-8174 + public void testChangeSignatureKeywordAndPositionalParameters() { + doInspectionTest(PyArgumentListInspection.class, "Change signature of f(x, foo, bar)", true, true); + } + + // PY-8174 + public void testChangeSignatureAddKeywordOnlyParameter() { + runWithLanguageLevel(LanguageLevel.PYTHON30, () -> { + doInspectionTest(PyArgumentListInspection.class, "Change signature of func(x, *args, foo, bar)", true, true); + }); + } + + // PY-8174 + public void testChangeSignatureNewParametersNames() { + doInspectionTest(PyArgumentListInspection.class, "Change signature of func(i1, i, i3, num)", true, true); + } + + // PY-8174 + public void testChangeSignatureParametersDefaultValues() { + doInspectionTest(PyArgumentListInspection.class, "Change signature of func(i, foo)", true, true); + } + @Override @NonNls protected String getTestDataPath() { @@ -690,7 +714,9 @@ public class PyQuickFixTest extends PyTestCase { final List intentionActions = myFixture.filterAvailableIntentions(quickFixName); if (available) { if (intentionActions.isEmpty()) { - throw new AssertionError("Quickfix \"" + quickFixName + "\" is not available"); + final List intentionNames = ContainerUtil.map(myFixture.getAvailableIntentions(), IntentionAction::getText); + throw new AssertionError("Quickfix starting with \"" + quickFixName + "\" is not available. " + + "Available intentions:\n" + StringUtil.join(intentionNames, "\n")); } if (intentionActions.size() > 1) { throw new AssertionError("There are more than one quickfix with the name \"" + quickFixName + "\""); diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index d6b65743325c..5a055a1fe49c 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -490,6 +490,94 @@ public class PyStubsTest extends PyTestCase { doTestUnsupportedNamedTuple(); } + public void testFullyQualifiedTypingNamedTuple() { + doTestNamedTuple( + QualifiedName.fromDottedString("typing.NamedTuple") + ); + } + + public void testFullyQualifiedTypingNamedTupleWithAs() { + doTestNamedTuple( + QualifiedName.fromDottedString("T.NamedTuple") + ); + } + + public void testImportedTypingNamedTuple() { + doTestNamedTuple( + QualifiedName.fromComponents("NamedTuple") + ); + } + + public void testImportedTypingNamedTupleWithAs() { + doTestNamedTuple( + QualifiedName.fromComponents("NT") + ); + } + + public void testTypingNamedTupleNameReference() { + doTestNamedTypingTupleArguments(); + } + + public void testTypingNamedTupleFieldsReference() { + doTestNamedTypingTupleArguments(); + } + + public void testTypingNamedTupleNameChain() { + doTestNamedTypingTupleArguments(); + } + + public void testTypingNamedTupleFieldsChain() { + doTestNamedTypingTupleArguments(); + } + + public void _testImportedTypingNamedTupleName() { + doTestUnsupportedNamedTuple(); + } + + public void _testImportedTypingNamedTupleFields() { + doTestUnsupportedNamedTuple(); + } + + public void testFullyQualifiedTypingNamedTupleKwargs() { + doTestNamedTuple( + QualifiedName.fromDottedString("typing.NamedTuple") + ); + } + + public void testFullyQualifiedTypingNamedTupleKwargsWithAs() { + doTestNamedTuple( + QualifiedName.fromDottedString("T.NamedTuple") + ); + } + + public void testImportedTypingNamedTupleKwargs() { + doTestNamedTuple( + QualifiedName.fromComponents("NamedTuple") + ); + } + + public void testImportedTypingNamedTupleKwargsWithAs() { + doTestNamedTuple( + QualifiedName.fromComponents("NT") + ); + } + + public void testTypingNamedTupleKwargsNameReference() { + doTestNamedTypingTupleArguments(); + } + + public void testTypingNamedTupleKwargsNameChain() { + doTestNamedTypingTupleArguments(); + } + + public void _testImportedTypingNamedTupleKwargsName() { + doTestUnsupportedNamedTuple(); + } + + public void _testImportedTypingNamedTupleKwargsFields() { + doTestUnsupportedNamedTuple(); + } + private void doTestNamedTuple(@NotNull QualifiedName expectedCalleeName) { doTestNamedTuple("name", Collections.singletonList("field"), expectedCalleeName); } @@ -498,6 +586,10 @@ public class PyStubsTest extends PyTestCase { doTestNamedTuple("name", Arrays.asList("x", "y"), QualifiedName.fromComponents("namedtuple")); } + private void doTestNamedTypingTupleArguments() { + doTestNamedTuple("name", Arrays.asList("x", "y"), QualifiedName.fromComponents("NamedTuple")); + } + private void doTestNamedTuple(@NotNull String expectedName, @NotNull List expectedFields, @NotNull QualifiedName expectedCalleeName) { diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 85fa183469b9..ddfea706620b 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -1677,6 +1677,18 @@ public class PyTypeTest extends PyTestCase { " return get_class()"); } + // PY-7322 + public void testNamedTupleParameterInDocString() { + doTest("Point", + "from collections import namedtuple\n" + + "Point = namedtuple('Point', ('x', 'y'))\n" + + "def takes_a_point(point):\n" + + " \"\"\"\n" + + " :type point: Point\n" + + " \"\"\"\n" + + " expr = point"); + } + // PY-22919 public void testMaxListKnownElements() { doTest("int", diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 480437f318ce..7496bec64430 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -1136,6 +1136,62 @@ public class PythonCompletionTest extends PyTestCase { assertDoesntContain(suggested, "_T", "_KT"); } + // PY-18246 + public void testTypingNamedTupleCreatedViaCallInstance() { + myFixture.copyDirectoryToProject("../typing", ""); + + final List suggested = doTestByText( + "from typing import NamedTuple\n" + + "EmployeeRecord = NamedTuple('EmployeeRecord', [\n" + + " ('name', str),\n" + + " ('age', int),\n" + + " ('title', str),\n" + + " ('department', str)\n" + + "])\n" + + "e = EmployeeRecord('n', 'a', 't', 'd')\n" + + "e." + ); + assertNotNull(suggested); + assertContainsElements(suggested, "name", "age", "title", "department"); + } + + // PY-18246 + public void testTypingNamedTupleCreatedViaKwargsCallInstance() { + myFixture.copyDirectoryToProject("../typing", ""); + + final List suggested = doTestByText( + "from typing import NamedTuple\n" + + "EmployeeRecord = NamedTuple('EmployeeRecord', name=str, age=int, title=str, department=str)\n" + + "e = EmployeeRecord('n', 'a', 't', 'd')\n" + + "e." + ); + assertNotNull(suggested); + assertContainsElements(suggested, "name", "age", "title", "department"); + } + + // PY-18246 + public void testTypingNamedTupleCreatedViaInheritanceInstance() { + runWithLanguageLevel( + LanguageLevel.PYTHON36, + () -> { + myFixture.copyDirectoryToProject("../typing", ""); + + final List suggested = doTestByText( + "from typing import NamedTuple\n" + + "class EmployeeRecord(NamedTuple):\n" + + " name: str\n" + + " age: int\n" + + " title: str\n" + + " department: str\n" + + "e = EmployeeRecord('n', 'a', 't', 'd')\n" + + "e." + ); + assertNotNull(suggested); + assertContainsElements(suggested, "name", "age", "title", "department"); + } + ); + } + // PY-21519 public void testTypeComment() { myFixture.copyFileToProject("../typing/typing.py"); diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index cbaef8617e4e..c118d3effe03 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -233,4 +233,8 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase { public void testGenericKwargs() { doTest(); } + + public void testTypingNamedTupleAsParameter() { + doTest(); + } } diff --git a/resources-en/src/inspectionDescriptions/BoxForComparison.html b/resources-en/src/inspectionDescriptions/BoxForComparison.html deleted file mode 100644 index ec2772159f8a..000000000000 --- a/resources-en/src/inspectionDescriptions/BoxForComparison.html +++ /dev/null @@ -1,10 +0,0 @@ - - -

This inspection suggests to use Integer.compare(), etc. static methods where - constructs involving boxing Integer.valueOf(x).compareTo(y) are used.

-

Double.compare and Float.compare methods appeared in Java 1.4, methods for other boxed types - are available since Java 1.7

- -

New in 2017.2

- - \ No newline at end of file diff --git a/resources-en/src/inspectionDescriptions/UseCompareMethod.html b/resources-en/src/inspectionDescriptions/UseCompareMethod.html new file mode 100644 index 000000000000..4321e3df8303 --- /dev/null +++ b/resources-en/src/inspectionDescriptions/UseCompareMethod.html @@ -0,0 +1,11 @@ + + +

This inspection suggests to use Integer.compare(), etc. static methods where more verbose or less efficient constructs are + used. For example, x > y ? 1 : x < y ? -1 : 0 or Integer.valueOf(x).compareTo(y) could be + replaced with Integer.compare(x, y).

+

Double.compare and Float.compare methods appeared in Java 1.4, methods for other primitive types + are available since Java 1.7

+ +

New in 2017.2

+ + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index e4d9b21a6d54..b5fb9748e057 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -769,9 +769,9 @@ - + implementationClass="com.intellij.codeInspection.UseCompareMethodInspection" /> - diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java index 0058cbe3babe..418da0e5308f 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java @@ -41,7 +41,7 @@ import org.jetbrains.annotations.NotNull; public class BreadcrumbsInitializingActivity implements StartupActivity, DumbAware { @Override public void runActivity(@NotNull Project project) { - if (project.isDefault() || ApplicationManager.getApplication().isUnitTestMode()) { + if (project.isDefault() || ApplicationManager.getApplication().isUnitTestMode() || project.isDisposed()) { return; } diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java index 372035d8794d..6006f1555319 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java @@ -48,6 +48,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.ui.Gray; import com.intellij.ui.breadcrumbs.BreadcrumbsProvider; +import com.intellij.ui.breadcrumbs.BreadcrumbsUtil; import com.intellij.ui.components.breadcrumbs.Crumb; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.MouseEventAdapter; @@ -241,7 +242,7 @@ public class BreadcrumbsXmlWrapper extends JComponent implements Disposable { private static BreadcrumbsProvider findProviderForElement(@NotNull PsiElement element, BreadcrumbsProvider defaultProvider) { Language language = element.getLanguage(); if (!EditorSettingsExternalizable.getInstance().isBreadcrumbsShownFor(language.getID())) return defaultProvider; - BreadcrumbsProvider provider = getInfoProvider(language); + BreadcrumbsProvider provider = BreadcrumbsUtil.getInfoProvider(language); return provider == null ? defaultProvider : provider; } @@ -418,11 +419,11 @@ public class BreadcrumbsXmlWrapper extends JComponent implements Disposable { Language baseLang = viewProvider.getBaseLanguage(); if (checkSettings && !settings.isBreadcrumbsShownFor(baseLang.getID())) return null; - BreadcrumbsProvider provider = getInfoProvider(baseLang); + BreadcrumbsProvider provider = BreadcrumbsUtil.getInfoProvider(baseLang); if (provider == null) { for (Language language : viewProvider.getLanguages()) { if (!checkSettings || settings.isBreadcrumbsShownFor(language.getID())) { - provider = getInfoProvider(language); + provider = BreadcrumbsUtil.getInfoProvider(language); if (provider != null) break; } } @@ -491,18 +492,6 @@ public class BreadcrumbsXmlWrapper extends JComponent implements Disposable { breadcrumbs.setCrumbs(null); } - @Nullable - private static BreadcrumbsProvider getInfoProvider(@NotNull Language language) { - for (BreadcrumbsProvider provider : BreadcrumbsProvider.EP_NAME.getExtensions()) { - for (Language supported : provider.getLanguages()) { - if (supported.isKindOf(language)) { - return provider; - } - } - } - return null; - } - private static class MyUpdate extends Update { private final BreadcrumbsXmlWrapper myBreadcrumbsComponent; diff --git a/xml/xml-psi-impl/src/com/intellij/application/options/editor/CssFoldingSettings.java b/xml/xml-psi-impl/src/com/intellij/application/options/editor/CssFoldingSettings.java deleted file mode 100644 index 264eb995b2be..000000000000 --- a/xml/xml-psi-impl/src/com/intellij/application/options/editor/CssFoldingSettings.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.application.options.editor; - -import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.components.State; -import com.intellij.openapi.components.Storage; -import com.intellij.util.xmlb.XmlSerializerUtil; -import org.jetbrains.annotations.Nullable; - -/** - * User: zolotov - * Date: 4/18/13 - * @deprecated use {@link XmlFoldingSettings} - * todo: delete after 2017.1 release - */ -@State( - name="CssFoldingSettings", - storages= { - @Storage(value = "editor.codeinsight.xml", deprecated = true)} -) -public class CssFoldingSettings implements PersistentStateComponent { - public static CssFoldingSettings getInstance() { - return ServiceManager.getService(CssFoldingSettings.class); - } - - private boolean myCollapseDataUri = true; - - public boolean isCollapseDataUri() { - return myCollapseDataUri; - } - - @SuppressWarnings("UnusedDeclaration") - public void setCollapseDataUri(boolean value) { - myCollapseDataUri = value; - } - - @Nullable - @Override - public CssFoldingSettings getState() { - return this; - } - - @Override - public void loadState(CssFoldingSettings state) { - XmlSerializerUtil.copyBean(state, this); - } -} diff --git a/xml/xml-psi-impl/src/com/intellij/application/options/editor/XmlFoldingSettings.java b/xml/xml-psi-impl/src/com/intellij/application/options/editor/XmlFoldingSettings.java index 54e8be90eef2..96873f18e36b 100644 --- a/xml/xml-psi-impl/src/com/intellij/application/options/editor/XmlFoldingSettings.java +++ b/xml/xml-psi-impl/src/com/intellij/application/options/editor/XmlFoldingSettings.java @@ -31,14 +31,6 @@ public class XmlFoldingSettings implements XmlCodeFoldingSettings, PersistentSta return ServiceManager.getService(XmlFoldingSettings.class); } - public XmlFoldingSettings() { - // todo: remove after 2017.1 release - CssFoldingSettings cssFoldingSettings = CssFoldingSettings.getInstance(); - if (cssFoldingSettings != null) { - myState.COLLAPSE_DATA_URI = cssFoldingSettings.isCollapseDataUri(); - } - } - @Override public boolean isCollapseXmlTags() { return myState.COLLAPSE_XML_TAGS;