diff --git a/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt b/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt index 675cef723f0e..e8927de8a0f7 100644 --- a/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt +++ b/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt @@ -17,13 +17,13 @@ package com.intellij.codeInsight.intention import com.intellij.lang.Language import com.intellij.lang.LanguageExtension -import com.intellij.psi.PsiModifier -import com.intellij.psi.PsiType -import com.intellij.psi.PsiTypeParameter +import com.intellij.openapi.components.ServiceManager +import com.intellij.psi.* import org.jetbrains.annotations.ApiStatus import org.jetbrains.uast.UClass import org.jetbrains.uast.UDeclaration -import org.jetbrains.uast.UParameter +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UastContext /** * Extension Point provides language-abstracted code modifications for JVM-based languages. @@ -38,18 +38,22 @@ import org.jetbrains.uast.UParameter @ApiStatus.Experimental abstract class JvmCommonIntentionActionsFactory { - open fun createChangeModifierAction(declaration: UDeclaration, + open fun createChangeModifierAction(declaration: @JvmCommon PsiModifierListOwner, @PsiModifier.ModifierConstant modifier: String, - shouldPresent: Boolean): IntentionAction? = null + shouldPresent: Boolean): IntentionAction? = + //Fallback if Uast-version of method is overridden + createChangeModifierAction(declaration.asUast(), modifier, shouldPresent) open fun createAddCallableMemberActions(info: MethodInsertionInfo): List = emptyList() - open fun createAddBeanPropertyActions(uClass: UClass, + open fun createAddBeanPropertyActions(psiClass: @JvmCommon PsiClass, propertyName: String, @PsiModifier.ModifierConstant visibilityModifier: String, propertyType: PsiType, setterRequired: Boolean, - getterRequired: Boolean): List = emptyList() + getterRequired: Boolean): List = + //Fallback if Uast-version of method is overridden + createAddBeanPropertyActions(psiClass.asUast(), propertyName, visibilityModifier, propertyType, setterRequired, getterRequired) companion object : LanguageExtension( "com.intellij.codeInsight.intention.jvmCommonIntentionActionsFactory") { @@ -58,49 +62,78 @@ abstract class JvmCommonIntentionActionsFactory { override fun forLanguage(l: Language): JvmCommonIntentionActionsFactory? = super.forLanguage(l) } + //A fallback to old api + @Deprecated("use or/and override @JvmCommon-version of this method instead") + open fun createChangeModifierAction(declaration: UDeclaration, + @PsiModifier.ModifierConstant modifier: String, + shouldPresent: Boolean): IntentionAction? = null + + @Deprecated("use or/and override @JvmCommon-version of this method instead") + open fun createAddBeanPropertyActions(uClass: UClass, + propertyName: String, + @PsiModifier.ModifierConstant visibilityModifier: String, + propertyType: PsiType, + setterRequired: Boolean, + getterRequired: Boolean): List = emptyList() + + } +@ApiStatus.Experimental sealed class MethodInsertionInfo( - val containingClass: UClass, + val targetClass: @JvmCommon PsiClass, @PsiModifier.ModifierConstant val modifiers: List = emptyList(), val typeParams: List = emptyList(), - val parameters: List = emptyList() + val parameters: List<@JvmCommon PsiParameter> = emptyList() ) { + + @Deprecated("use `targetClass`", ReplaceWith("targetClass")) + val containingClass: UClass + get() = targetClass.asUast() + companion object { @JvmStatic - fun constructorInfo(containingClass: UClass, parameters: List) = - Constructor(containingClass = containingClass, parameters = parameters) + fun constructorInfo(targetClass: @JvmCommon PsiClass, parameters: List<@JvmCommon PsiParameter>) = + Constructor(targetClass = targetClass, parameters = parameters) @JvmStatic - fun simpleMethodInfo(containingClass: UClass, + fun simpleMethodInfo(containingClass: @JvmCommon PsiClass, methodName: String, @PsiModifier.ModifierConstant modifier: String, returnType: PsiType, - parameters: List) = + parameters: List<@JvmCommon PsiParameter>) = Method(name = methodName, modifiers = listOf(modifier), - containingClass = containingClass, + targetClass = containingClass, returnType = returnType, parameters = parameters) + } class Method( - containingClass: UClass, + targetClass: @JvmCommon PsiClass, val name: String, modifiers: List = emptyList(), - typeParams: List = emptyList(), + typeParams: List<@JvmCommon PsiTypeParameter> = emptyList(), val returnType: PsiType, - parameters: List = emptyList(), + parameters: List<@JvmCommon PsiParameter> = emptyList(), val isAbstract: Boolean = false - ) : MethodInsertionInfo(containingClass, modifiers, typeParams, parameters) + ) : MethodInsertionInfo(targetClass, modifiers, typeParams, parameters) class Constructor( - containingClass: UClass, + targetClass: @JvmCommon PsiClass, modifiers: List = emptyList(), - typeParams: List = emptyList(), - parameters: List = emptyList() - ) : MethodInsertionInfo(containingClass, modifiers, typeParams, parameters) + typeParams: List<@JvmCommon PsiTypeParameter> = emptyList(), + parameters: List<@JvmCommon PsiParameter> = emptyList() + ) : MethodInsertionInfo(targetClass, modifiers, typeParams, parameters) +} + +@Deprecated("remove after kotlin plugin will be ported") +private inline fun PsiElement.asUast(): T = when (this) { + is T -> this + else -> this.let { ServiceManager.getService(project, UastContext::class.java).convertElement(this, null, T::class.java) as T? } + ?: throw UnsupportedOperationException("cant convert $this to ${T::class}") } \ No newline at end of file diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt index 54bfa0647d3c..7f86add69759 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt @@ -17,8 +17,8 @@ package com.intellij.codeInspection.inheritance import com.intellij.CommonBundle import com.intellij.codeInsight.daemon.QuickFixBundle -import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory import com.intellij.codeInspection.* import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.Logger @@ -185,7 +185,8 @@ class ImplicitSubclassInspection : AbstractBaseUastLocalInspectionTool() { shouldPresent: Boolean, actionsList: SmartList) { if (declaration.modifierList?.hasModifierProperty(name) != shouldPresent) { - (actionsFactory.createChangeModifierAction(declaration, name, shouldPresent))?.let { + (actionsFactory.createChangeModifierAction(declaration as @com.intellij.psi.JvmCommon PsiModifierListOwner, name, + shouldPresent))?.let { actionsList.add(it) } } diff --git a/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java b/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java index ac3a060c2ab2..b7593b26aa87 100644 --- a/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java +++ b/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java @@ -225,22 +225,27 @@ public class ParametersFolder { if (isAccessedForWriting((PsiExpression)expression)) { return null; } - for (PsiElement scopeElement : scopeElements) { - if (PsiTreeUtil.isAncestor(expression, scopeElement, true)) { - expression = null; - break; - } + if (isAncestor(expression, scopeElements)) { + break; + } + if (dependsOnLocals(expression, inputVariables)) { + break; + } + final PsiElement parent = expression.getParent(); + if (parent instanceof PsiExpressionStatement) { + break; } - if (expression == null) break; - final PsiType expressionType = ((PsiExpression)expression).getType(); - if (expressionType != null && !PsiType.VOID.equals(expressionType) && !(expression.getParent() instanceof PsiExpressionStatement)) { - if (dependsOnLocals(expression, inputVariables)) { - break; - } + if (expressionType == null || PsiType.VOID.equals(expressionType)) { + break; + } + if (isTooLongExpressionChain(expression)) { + break; + } + if (!isMethodNameExpression(expression)) { expressions.add((PsiExpression)expression); } - expression = PsiTreeUtil.getParentOfType(expression, PsiExpression.class); + expression = parent instanceof PsiExpression ? parent : null; } } else { @@ -275,6 +280,47 @@ public class ParametersFolder { return exprWithWriteAccessInside[0] != null; } + private static boolean isAncestor(PsiElement expression, PsiElement[] scopeElements) { + for (PsiElement scopeElement : scopeElements) { + if (PsiTreeUtil.isAncestor(expression, scopeElement, true)) { + return true; + } + } + return false; + } + + private static boolean isTooLongExpressionChain(PsiElement expression) { + int count = 0; + for (PsiElement element = getInnerExpression(expression); element != null; element = getInnerExpression(element)) { + count++; + if (count > 1) { // expression chains like 'var.foo().bar()' and 'var.foo[i].bar()' are too long + return true; + } + } + return false; + } + + private static PsiElement getInnerExpression(PsiElement expression) { + if (expression instanceof PsiMethodCallExpression) { + return ((PsiMethodCallExpression)expression).getMethodExpression().getQualifierExpression(); + } + if (expression instanceof PsiArrayAccessExpression) { + while (expression instanceof PsiArrayAccessExpression) { + expression = ((PsiArrayAccessExpression)expression).getArrayExpression(); + } + return expression; + } + return null; + } + + private static boolean isMethodNameExpression(@NotNull PsiElement expression) { + final PsiElement parent = expression.getParent(); + return expression instanceof PsiReferenceExpression && + parent instanceof PsiMethodCallExpression && + ((PsiReferenceExpression)expression).getReferenceNameElement() == + ((PsiMethodCallExpression)parent).getMethodExpression().getReferenceNameElement(); + } + private static boolean dependsOnLocals(final PsiElement expression, final List inputVariables) { final boolean[] localVarsUsed = new boolean[]{false}; expression.accept(new JavaRecursiveElementWalkingVisitor(){ diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/UastCreateMethodFix.kt b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFix.kt similarity index 70% rename from java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/UastCreateMethodFix.kt rename to java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFix.kt index fba05a6ef16e..798202471e86 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/UastCreateMethodFix.kt +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFix.kt @@ -22,15 +22,13 @@ import com.intellij.codeInspection.LocalQuickFixBase import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.project.Project -import com.intellij.psi.PsiModifier -import com.intellij.psi.PsiType -import com.intellij.psi.SmartPointerManager +import com.intellij.psi.* import com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.util.PsiNavigateUtil -import org.jetbrains.uast.UClass import org.jetbrains.uast.UMethod +import org.jetbrains.uast.toUElementOfType -class UastCreateMethodFix(containingClass: UClass, private val createMethodAction: IntentionAction) +class CreateMethodFix(containingClass: @JvmCommon PsiClass, private val createMethodAction: IntentionAction) : LocalQuickFixBase(createMethodAction.text, createMethodAction.familyName) { private val containingClass = SmartPointerManager.getInstance(containingClass.project).createSmartPsiElementPointer(containingClass) @@ -44,22 +42,27 @@ class UastCreateMethodFix(containingClass: UClass, private val createMethodActio reformatAndOpenCreatedMethod(newMethod) } - private fun reformatAndOpenCreatedMethod(method: UMethod) { + private fun reformatAndOpenCreatedMethod(method: @JvmCommon PsiMethod) { CodeStyleManager.getInstance(containingClass.project).reformat(method) - PsiNavigateUtil.navigate(method.uastBody?.psi?.lastChild ?: method) + PsiNavigateUtil.navigate((method.body ?: uastBody(method))?.lastChild ?: method) + } + + private fun uastBody(method: PsiMethod): PsiElement? = when (method) { + is UMethod -> method.uastBody?.psi + else -> method.toUElementOfType()?.uastBody?.psi } companion object { @JvmStatic - fun createVoidMethodIfFixPossible(uClass: UClass, + fun createVoidMethodIfFixPossible(psiClass: @JvmCommon PsiClass, methodName: String, - @PsiModifier.ModifierConstant modifier: String): UastCreateMethodFix? { - if (!ModuleUtilCore.projectContainsFile(uClass.project, uClass.containingFile.virtualFile, false)) return null - val actionsFactory = JvmCommonIntentionActionsFactory.forLanguage(uClass.language) ?: return null + @PsiModifier.ModifierConstant modifier: String): CreateMethodFix? { + if (!ModuleUtilCore.projectContainsFile(psiClass.project, psiClass.containingFile.virtualFile, false)) return null + val actionsFactory = JvmCommonIntentionActionsFactory.forLanguage(psiClass.language) ?: return null val action = actionsFactory.createAddCallableMemberActions( - MethodInsertionInfo.simpleMethodInfo(uClass, methodName, modifier, PsiType.VOID, emptyList()) + MethodInsertionInfo.simpleMethodInfo(psiClass, methodName, modifier, PsiType.VOID, emptyList()) ).firstOrNull() ?: return null - return UastCreateMethodFix(uClass, action) + return CreateMethodFix(psiClass, action) } } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt b/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt index abeab071f942..e27970f05a7d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt @@ -22,46 +22,47 @@ import com.intellij.codeInsight.intention.AbstractIntentionAction import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory import com.intellij.codeInsight.intention.MethodInsertionInfo +import com.intellij.lang.java.JavaLanguage +import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.* +import com.intellij.psi.codeStyle.JavaCodeStyleManager import com.intellij.psi.impl.beanProperties.CreateJavaBeanPropertyFix import com.intellij.psi.util.PsiFormatUtil import com.intellij.psi.util.PsiFormatUtilBase import com.intellij.util.VisibilityUtil import org.jetbrains.annotations.NotNull -import org.jetbrains.uast.UClass -import org.jetbrains.uast.UDeclaration -import org.jetbrains.uast.UParameter - class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { - override fun createChangeModifierAction(declaration: UDeclaration, modifier: String, shouldPresent: Boolean): IntentionAction { + override fun createChangeModifierAction(declaration: @JvmCommon PsiModifierListOwner, + modifier: String, + shouldPresent: Boolean): IntentionAction { return ModifierFix(declaration.modifierList, modifier, shouldPresent, false) } override fun createAddCallableMemberActions(info: MethodInsertionInfo): List { return when (info) { is MethodInsertionInfo.Method -> with(info) { - createAddMethodAction(containingClass, name, modifiers.joinToString(" "), returnType, parameters) + createAddMethodAction(targetClass, name, modifiers.joinToString(" "), returnType, parameters) ?.let { listOf(it) } ?: emptyList() } is MethodInsertionInfo.Constructor -> - listOf(AddConstructorFix(info.containingClass.psi, info.parameters.map { it.psi })) + listOf(AddConstructorFix(info.targetClass.javaPsi(), info.parameters.map { it.javaPsi() })) } } - private fun createAddMethodAction(uClass: UClass, + private fun createAddMethodAction(psiClass: @JvmCommon PsiClass, methodName: String, @PsiModifier.ModifierConstant @NotNull visibilityModifier: String, returnType: PsiType, - parameters: List): IntentionAction? { - val paramsString = parameters.mapIndexed { i, t -> "${t.type.presentableText} ${t.name ?: "arg$i"}" }.joinToString() + parameters: List<@JvmCommon PsiParameter>): IntentionAction? { + val paramsString = parameters.mapIndexed { i, t -> "${t.type.canonicalText} ${t.name ?: "arg$i"}" }.joinToString() val signatureString = - "${VisibilityUtil.getVisibilityString(visibilityModifier)} ${returnType.presentableText} $methodName($paramsString){}" - val targetClassPointer = SmartPointerManager.getInstance(uClass.project).createSmartPsiElementPointer(uClass.psi) + "${VisibilityUtil.getVisibilityString(visibilityModifier)} ${returnType.canonicalText} $methodName($paramsString){}" + val targetClassPointer = SmartPointerManager.getInstance(psiClass.project).createSmartPsiElementPointer(psiClass.javaPsi()) return object : AbstractIntentionAction() { private val text = targetClassPointer.element?.let { psiClass -> @@ -77,17 +78,30 @@ class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { override fun getText(): String = text override fun invoke(project: Project, editor: Editor?, file: PsiFile) { - targetClassPointer.element?.let { targetClass -> - targetClass.add(createMethod(targetClass)) + val targetClass = targetClassPointer.element ?: return + runWriteAction { + val addedMethod = targetClass.add(createMethod(targetClass)) + JavaCodeStyleManager.getInstance(project).shortenClassReferences(addedMethod) } } - private fun createMethod(targetClass: PsiClass): PsiMethod = PsiElementFactory.SERVICE.getInstance(targetClass.project) - .createMethodFromText(signatureString, targetClass) + private fun createMethod(targetClass: PsiClass): PsiMethod { + val elementFactory = JVMElementFactories.getFactory(targetClass.language, targetClass.project) // it could be Groovy + ?: JavaPsiFacade.getElementFactory(targetClass.project) + return elementFactory.createMethodFromText(signatureString, targetClass) + } } } - override fun createAddBeanPropertyActions(uClass: UClass, + private inline fun T.javaPsi(): T { + assert(language == JavaLanguage.INSTANCE) + return when (this) { + is org.jetbrains.uast.UElement -> psi as T + else -> this + } + } + + override fun createAddBeanPropertyActions(psiClass: @JvmCommon PsiClass, propertyName: String, @PsiModifier.ModifierConstant visibilityModifier: String, propertyType: PsiType, @@ -95,20 +109,20 @@ class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { getterRequired: Boolean): List { if (getterRequired && setterRequired) return listOf( - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, true), - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, false)) if (getterRequired || setterRequired) return listOf( - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, true), - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, false), - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, true, true, true)) + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, true, true, true)) return listOf( - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, true)) + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, true)) } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java b/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java index b300bb34803e..1f1cd47a37ca 100644 --- a/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java +++ b/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java @@ -25,8 +25,6 @@ import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.uast.UClass; -import org.jetbrains.uast.UastContextKt; import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING; import static com.intellij.util.ArrayUtil.toObjectArray; @@ -35,14 +33,14 @@ import static com.intellij.util.ArrayUtil.toObjectArray; public class CreateBeanPropertyFixes { public static LocalQuickFix[] createFixes(String propertyName, - @NotNull PsiClass psiClass, + @NotNull @JvmCommon PsiClass psiClass, @Nullable PsiType type, final boolean createSetter) { return IntentionWrapper.wrapToQuickFixes(createActions(propertyName, psiClass, type, createSetter), psiClass.getContainingFile()); } public static IntentionAction[] createActions(String propertyName, - @NotNull PsiClass psiClass, + @NotNull @JvmCommon PsiClass psiClass, @Nullable PsiType type, final boolean createSetter) { if (psiClass instanceof PsiCompiledElement) return IntentionAction.EMPTY_ARRAY; @@ -55,9 +53,8 @@ public class CreateBeanPropertyFixes { } JvmCommonIntentionActionsFactory factory = JvmCommonIntentionActionsFactory.forLanguage(psiClass.getLanguage()); if (factory == null) return IntentionAction.EMPTY_ARRAY; - UClass uClass = UastContextKt.toUElement(psiClass, UClass.class); - if (uClass == null) return IntentionAction.EMPTY_ARRAY; - return toObjectArray(factory.createAddBeanPropertyActions(uClass, propertyName, PsiModifier.PUBLIC, type, createSetter, !createSetter), - IntentionAction.class); + return toObjectArray( + factory.createAddBeanPropertyActions(psiClass, propertyName, PsiModifier.PUBLIC, type, createSetter, !createSetter), + IntentionAction.class); } } diff --git a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java index 577ec002e58c..7083aede4b4a 100644 --- a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java @@ -947,8 +947,9 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr RefactoringUtil.fixJavadocsForParams(method, newParameters, pair -> { final PsiParameter parameter = pair.first; final String oldParamName = pair.second; - final int idx = ArrayUtil.find(oldParameterNames, oldParamName); - return idx >= 0 && idx == method.getParameterList().getParameterIndex(parameter) && changeInfo.getNewParameters()[idx].getOldIndex() == idx; + final int oldIdx = ArrayUtil.find(oldParameterNames, oldParamName); + int newIndex = method.getParameterList().getParameterIndex(parameter); + return oldIdx >= 0 && newIndex >= 0 && changeInfo.getNewParameters()[newIndex].getOldIndex() == oldIdx; }, paramName -> ArrayUtil.find(oldParameterNames, paramName) >= 0); } diff --git a/java/java-psi-api/src/com/intellij/psi/JvmCommon.java b/java/java-psi-api/src/com/intellij/psi/JvmCommon.java new file mode 100644 index 000000000000..a643491097f4 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/JvmCommon.java @@ -0,0 +1,41 @@ +/* + * 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.psi; + +import org.jetbrains.annotations.ApiStatus; + +import java.lang.annotation.*; + +/** + * Annotation for marking {@link PsiElement} subclasses usages that they will be used in + * language-abstracted way: + *
    + *
  • No modification methods should be directly called on such instances
  • + *
  • Usages should be aware of {@link PsiElement#getLanguage()}, and not be hardcoded to dedicated language
  • + *
  • Instances could be "virtual" and don't have physical representations
  • + *
  • Complex operations should be performed via corresponding {@link com.intellij.lang.LanguageExtensionPoint} or similar API
  • + *
  • {@link org.jetbrains.uast.UClass}-like instances should be supported
  • + *
+ * Note: this annotation considered as kind-of "transitional" until generic language-independent API will be implemented + */ +@ApiStatus.Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ + ElementType.TYPE, ElementType.PARAMETER, ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER +}) +public @interface JvmCommon { +} diff --git a/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered.java b/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered.java new file mode 100644 index 000000000000..77faca83f8fa --- /dev/null +++ b/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered.java @@ -0,0 +1,8 @@ +class X { + + /** + * @param a aparam + * @param b bparam + */ + public void mymethod(boolean a, boolean b) { } +} diff --git a/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered_after.java b/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered_after.java new file mode 100644 index 000000000000..dc9349153919 --- /dev/null +++ b/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered_after.java @@ -0,0 +1,9 @@ +class X { + + /** + * @param a aparam + * @param c + * @param b1 bparam + */ + public void mymethod(boolean a, boolean c, boolean b1) { } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess.java new file mode 100644 index 000000000000..4c8b730e589c --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess.java @@ -0,0 +1,11 @@ +class Foo { + boolean bar(String[][] a) { + for (int i = 0; i < a.length; i++) + for (int j = 0; i < a[i].length; j++) { + if (a[i][j].length() > 3 && i % 3 == 0) + return true; + + } + return false; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess_after.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess_after.java new file mode 100644 index 000000000000..77a7b3a87345 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess_after.java @@ -0,0 +1,16 @@ +class Foo { + boolean bar(String[][] a) { + for (int i = 0; i < a.length; i++) + for (int j = 0; i < a[i].length; j++) { + if (newMethod(a[i][j], i)) return true; + + } + return false; + } + + private boolean newMethod(String s, int i) { + if (s.length() > 3 && i % 3 == 0) + return true; + return false; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess.java new file mode 100644 index 000000000000..3f2be2ea0d65 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess.java @@ -0,0 +1,15 @@ +class UseBuilder { + void test(Builder builder, int[] arr) { + builder.foo("xyz").bar(arr[0]).foo("abc"); + } + + static class Builder { + Builder foo(String s) { + return this; + } + + Builder bar(int x) { + return this; + } + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr.java new file mode 100644 index 000000000000..11f0607aaf81 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr.java @@ -0,0 +1,15 @@ +class UseBuilder { + void test(Builder builder, int[] arr) { + builder.foo("xyz").bar(arr[0]).foo("abc"); + } + + static class Builder { + Builder foo(String s) { + return this; + } + + Builder bar(int x) { + return this; + } + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr_after.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr_after.java new file mode 100644 index 000000000000..0ad6a1d75ad0 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr_after.java @@ -0,0 +1,19 @@ +class UseBuilder { + void test(Builder builder, int[] arr) { + newMethod(builder, arr[0]).foo("abc"); + } + + private Builder newMethod(Builder builder, int x) { + return builder.foo("xyz").bar(x); + } + + static class Builder { + Builder foo(String s) { + return this; + } + + Builder bar(int x) { + return this; + } + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf.java new file mode 100644 index 000000000000..d5a624418922 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf.java @@ -0,0 +1,9 @@ +class Foo { + boolean bar(String[] a) { + for (int i = 0; i < a.length; i++) { + if (a[i].length() > 3 && i % 3 == 0) + return true; + } + return false; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf_after.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf_after.java new file mode 100644 index 000000000000..f6f7323514f8 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf_after.java @@ -0,0 +1,14 @@ +class Foo { + boolean bar(String[] a) { + for (int i = 0; i < a.length; i++) { + if (newMethod(a[i], i)) return true; + } + return false; + } + + private boolean newMethod(String s, int i) { + if (s.length() > 3 && i % 3 == 0) + return true; + return false; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess_after.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess_after.java new file mode 100644 index 000000000000..3e4109c1c899 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess_after.java @@ -0,0 +1,19 @@ +class UseBuilder { + void test(Builder builder, int[] arr) { + newMethod(builder, arr[0]); + } + + private void newMethod(Builder builder, int x) { + builder.foo("xyz").bar(x).foo("abc"); + } + + static class Builder { + Builder foo(String s) { + return this; + } + + Builder bar(int x) { + return this; + } + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement.java b/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement.java new file mode 100644 index 000000000000..cf33c80c42d4 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement.java @@ -0,0 +1,5 @@ +class C { + int foo(String[][] vars, int i, int j) { + return vars[i][j].length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement_after.java b/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement_after.java new file mode 100644 index 000000000000..495f7d8f629e --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement_after.java @@ -0,0 +1,9 @@ +class C { + int foo(String[][] vars, int i, int j) { + return newMethod(vars[i][j]); + } + + private int newMethod(String s) { + return s.length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement.java b/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement.java new file mode 100644 index 000000000000..7794ab9692f3 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement.java @@ -0,0 +1,5 @@ +class C { + int foo(String[] vars, int i) { + return vars[i].length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement_after.java b/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement_after.java new file mode 100644 index 000000000000..5c74700b8918 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement_after.java @@ -0,0 +1,9 @@ +class C { + int foo(String[] vars, int i) { + return newMethod(vars[i]); + } + + private int newMethod(String var) { + return var.length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement.java b/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement.java new file mode 100644 index 000000000000..c5aca75e0a9d --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement.java @@ -0,0 +1,6 @@ +class C { + String[] vars; + int foo(C c, int i) { + return c.vars[i].length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement_after.java b/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement_after.java new file mode 100644 index 000000000000..569e9b96299c --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement_after.java @@ -0,0 +1,10 @@ +class C { + String[] vars; + int foo(C c, int i) { + return newMethod(c.vars[i]); + } + + private int newMethod(String var) { + return var.length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/InferencePerformanceTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/InferencePerformanceTest.java index 8b2a43e68f4f..e1695f605713 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/InferencePerformanceTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/InferencePerformanceTest.java @@ -25,11 +25,11 @@ public class InferencePerformanceTest extends LightDaemonAnalyzerTestCase { @NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/performance"; public void testPolyMethodCallArgumentPassedToVarargs() throws Exception { - PlatformTestUtil.startPerformanceTest("50 poly method calls passed to Arrays.asList", 3500, this::doTest).assertTiming(); + PlatformTestUtil.startPerformanceTest("50 poly method calls passed to Arrays.asList", 4000, this::doTest).usesAllCPUCores().assertTiming(); } public void testDiamondConstructorCallPassedToVarargs() throws Exception { - PlatformTestUtil.startPerformanceTest("50 diamond constructor calls passed to Arrays.asList", 10000, this::doTest).assertTiming(); + PlatformTestUtil.startPerformanceTest("50 diamond constructor calls passed to Arrays.asList", 12000, this::doTest).usesAllCPUCores().assertTiming(); } private void doTest() { diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java index 2e3d882359d1..c3613c86b4cf 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java @@ -226,6 +226,14 @@ public class ChangeSignatureTest extends ChangeSignatureBaseTest { }, false); } + public void testParamJavadocRenamedReordered() { + doTest(null, new ParameterInfoImpl[]{ + new ParameterInfoImpl(0, "a", PsiType.BOOLEAN), + new ParameterInfoImpl(-1, "c", PsiType.BOOLEAN), + new ParameterInfoImpl(1, "b1", PsiType.BOOLEAN), + }, false); + } + public void testJavadocNoNewLineInserted() { doTest(null, new ParameterInfoImpl[]{ new ParameterInfoImpl(0, "newArgs", PsiType.DOUBLE), diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodTest.java index e768c0be364f..369f866f9b1b 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodTest.java @@ -974,6 +974,34 @@ public class ExtractMethodTest extends LightCodeInsightTestCase { doTest(); } + public void testBuilderChainWithArrayAccess() throws Exception { + doTest(); + } + + public void testBuilderChainWithArrayAccessExpr() throws Exception { + doTest(); + } + + public void testBuilderChainWithArrayAccessIf() throws Exception { + doTest(); + } + + public void testBuilderChainWith2DimArrayAccess() throws Exception { + doTest(); + } + + public void testCallOnArrayElement() throws Exception { + doTest(); + } + + public void testCallOn2DimArrayElement() throws Exception { + doTest(); + } + + public void testCallOnFieldArrayElement() throws Exception { + doTest(); + } + private void doTestDisabledParam() throws PrepareFailedException { final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject()); settings.ELSE_ON_NEW_LINE = true; diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java index 15607af49e36..e43d13eddc06 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java @@ -511,7 +511,7 @@ public class PluginManagerCore { @NotNull private static ClassLoader[] getParentLoaders(@NotNull Map idToDescriptorMap, @NotNull PluginId[] pluginIds) { if (isUnitTestMode()) return new ClassLoader[0]; - final List classLoaders = new ArrayList<>(); + LinkedHashSet loaders = new LinkedHashSet<>(pluginIds.length); for (final PluginId id : pluginIds) { IdeaPluginDescriptor pluginDescriptor = idToDescriptorMap.get(id); if (pluginDescriptor == null) { @@ -522,9 +522,9 @@ public class PluginManagerCore { if (loader == null) { getLogger().error("Plugin class loader should be initialized for plugin " + id); } - classLoaders.add(loader); + loaders.add(loader); } - return classLoaders.toArray(new ClassLoader[classLoaders.size()]); + return loaders.toArray(new ClassLoader[loaders.size()]); } private static int countPlugins(@NotNull String pluginsPath) { diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt index 0d0cd1215662..5056d96884f3 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt @@ -378,9 +378,9 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { lines_inner { ("A B_C D" - "A_B C_D") - (" -- - " - " -- - ").default() - (" -- - " - " -- ").trim() - (" - " - " - ").ignore() + (" - -- " - " - -- ").default() + (" - -- " - " -- ").trim() + (" - " - " - ").ignore() testAll() } diff --git a/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt b/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt index 88c9fd49b069..ef765b7d5306 100644 --- a/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt @@ -593,7 +593,8 @@ class MergeTest : MergeTestBase() { 5 ====== modify 6 ====== - 7 ======""".trimIndent() + 7 ====== + 8 ======""".trimIndent() val text2 = """ 1 ====== 2 ====== @@ -604,8 +605,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent() + 8 ======""".trimIndent() val text3 = """ 1 ====== 2 ====== @@ -616,8 +618,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify right 6 ====== + 7 ====== modify - 7 ======""".trimIndent() + 8 ======""".trimIndent() testN(text1, text2, text3) { checkUndo(1) { @@ -636,8 +639,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify right 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent()) + 8 ======""".trimIndent()) } testN(text1, text2, text3) { @@ -658,8 +662,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent()) + 8 ======""".trimIndent()) } testN(text1, text2, text3) { @@ -678,8 +683,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify right 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent()) + 8 ======""".trimIndent()) } testN(text1, text2, text3) { @@ -701,8 +707,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify right 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent()) + 8 ======""".trimIndent()) } testN(text1, text2, text3) { @@ -723,8 +730,9 @@ class MergeTest : MergeTestBase() { 5 ====== USER modify 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent()) + 8 ======""".trimIndent()) } } } diff --git a/platform/diff-impl/tests/com/intellij/diff/util/DiffPerformanceTest.kt b/platform/diff-impl/tests/com/intellij/diff/util/DiffPerformanceTest.kt index 36e7b9d4e285..e94117e23be0 100644 --- a/platform/diff-impl/tests/com/intellij/diff/util/DiffPerformanceTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/util/DiffPerformanceTest.kt @@ -17,7 +17,9 @@ package com.intellij.diff.util import com.intellij.testFramework.PlatformTestUtil +import com.intellij.util.containers.StringInterner import com.intellij.util.diff.Diff +import com.intellij.util.diff.FilesTooBigForDiffException import junit.framework.TestCase import java.util.* @@ -26,7 +28,13 @@ class DiffPerformanceTest : TestCase() { private var needWarmUp = true } - private val arr_20000 = generateData(20000).toTypedArray() + private val interner: StringInterner = StringInterner(); + + val data = generateData(2000000) + private val arr_200000 = data.take(200000).toTypedArray() + private val arr_50000 = data.take(50000).toTypedArray() + private val arr_20000 = data.take(20000).toTypedArray() + private val arr_5000 = data.take(5000).toTypedArray() private val arr_2000 = arr_20000.take(2000).toTypedArray() private val arr_1000 = arr_20000.take(1000).toTypedArray() private val arr_100 = arr_20000.take(100).toTypedArray() @@ -35,16 +43,22 @@ class DiffPerformanceTest : TestCase() { private val shuffled_1000 = shuffled_2000.take(1000).toTypedArray() private val shuffled_100 = shuffled_2000.take(100).toTypedArray() + private val altered_200000 = alter(arr_200000) + private val altered_50000 = alter(arr_50000) private val altered_20000 = alter(arr_20000) private val altered_2000 = alter(arr_2000) private val altered_1000 = alter(arr_1000) private val altered_100 = alter(arr_100) + private val heavy_altered_200000 = heavy_alter(arr_200000) + private val heavy_altered_50000 = heavy_alter(arr_50000) private val heavy_altered_20000 = heavy_alter(arr_20000) private val heavy_altered_2000 = heavy_alter(arr_2000) private val heavy_altered_1000 = heavy_alter(arr_1000) private val heavy_altered_100 = heavy_alter(arr_100) + private val reversed_50000 = arr_50000.reversedArray() + private val reversed_5000 = arr_5000.reversedArray() private val reversed_2000 = arr_2000.reversedArray() private val reversed_1000 = arr_1000.reversedArray() private val reversed_100 = arr_100.reversedArray() @@ -58,19 +72,61 @@ class DiffPerformanceTest : TestCase() { } private fun warmUp() { - for (i in 0..20) { - Diff.buildChanges(arr_2000, shuffled_2000) + for (i in 0..40) { + Diff.buildChanges(arr_20000, heavy_altered_20000) + } + } + + fun `test altered 200000`() { + testCpu(3, 550) { + Diff.buildChanges(arr_200000, altered_200000) + } + } + + fun `test heavy altered 200000`() { + testCpu(1, 2900) { + Diff.buildChanges(arr_200000, heavy_altered_200000) + } + } + + fun `test reversed 50000 failure`() { + testCpu(1, 8000) { + try { + Diff.buildChanges(arr_50000, reversed_50000) + } + catch (e: FilesTooBigForDiffException) { + return@testCpu + } + fail("FilesTooBigForDiffException expected") + } + } + + fun `test reversed 5000`() { + testCpu(1, 2000) { + Diff.buildChanges(arr_5000, reversed_5000) + } + } + + fun `test altered 50000`() { + testCpu(20, 650) { + Diff.buildChanges(arr_50000, altered_50000) + } + } + + fun `test heavy altered 50000`() { + testCpu(3, 600) { + Diff.buildChanges(arr_50000, heavy_altered_50000) } } fun `test altered 20000`() { - testCpu(20, 400) { + testCpu(20, 350) { Diff.buildChanges(arr_20000, altered_20000) } } fun `test heavy altered 20000`() { - testCpu(10, 550) { + testCpu(15, 600) { Diff.buildChanges(arr_20000, heavy_altered_20000) } } @@ -88,13 +144,13 @@ class DiffPerformanceTest : TestCase() { } fun `test shuffled 2000`() { - testCpu(1, 700) { + testCpu(1, 500) { Diff.buildChanges(arr_2000, shuffled_2000) } } fun `test reversed 2000`() { - testCpu(1, 700) { + testCpu(1, 500) { Diff.buildChanges(arr_2000, reversed_2000) } } @@ -112,19 +168,19 @@ class DiffPerformanceTest : TestCase() { } fun `test shuffled 1000`() { - testCpu(5, 500) { + testCpu(10, 750) { Diff.buildChanges(arr_1000, shuffled_1000) } } fun `test reversed 1000`() { - testCpu(5, 550) { + testCpu(10, 750) { Diff.buildChanges(arr_1000, reversed_1000) } } fun `test altered 100`() { - testCpu(10000, 600) { + testCpu(10000, 550) { Diff.buildChanges(arr_100, altered_100) } } @@ -142,14 +198,14 @@ class DiffPerformanceTest : TestCase() { } fun `test reversed 100`() { - testCpu(500, 450) { + testCpu(1000, 500) { Diff.buildChanges(arr_100, reversed_100) } } private fun generateData(size: Int): List { - return (1..size).map { "${it % 200}" } + return (1..size).map { interner.intern("${it % 200}") } } private fun alter(arr: Array): Array { @@ -163,7 +219,7 @@ class DiffPerformanceTest : TestCase() { private fun heavy_alter(arr: Array): Array { val altered = arr.copyOf() for (i in 1..altered.lastIndex step 20) { - altered[i] = "${i % 200}" + altered[i] = interner.intern("${i % 200}") } altered[0] = "===" // avoid "common prefix/suffix" optimisation altered[altered.lastIndex] = "===" 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 86fdc2cf85f9..821df50c7785 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java @@ -48,10 +48,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; +import java.awt.event.*; import java.util.List; import static com.intellij.util.ui.UIUtil.DEFAULT_HGAP; @@ -133,29 +130,24 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private void trackDimensions(@Nullable String dimensionKey) { Window popupWindow = getPopupWindow(); if (popupWindow == null) return; - popupWindow.addComponentListener(new ComponentAdapter() { + ComponentListener windowListener = new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { if (myShown) { processOnSizeChanged(); } } - + }; + popupWindow.addComponentListener(windowListener); + addPopupListener(new JBPopupAdapter() { @Override - public void componentHidden(ComponentEvent e) { - popupWindow.removeComponentListener(this); + public void onClosed(LightweightWindowEvent event) { + popupWindow.removeComponentListener(windowListener); + if (dimensionKey != null && myUserSizeChanged) { + WindowStateService.getInstance(myProject).putSizeFor(myProject, dimensionKey, myPrevSize); + } } }); - if (dimensionKey != null) { - addPopupListener(new JBPopupAdapter() { - @Override - public void onClosed(LightweightWindowEvent event) { - if (myUserSizeChanged) { - WindowStateService.getInstance(myProject).putSizeFor(myProject, dimensionKey, myPrevSize); - } - } - }); - } } private void processOnSizeChanged() { diff --git a/platform/icons/src/vcs/patch.png b/platform/icons/src/vcs/patch.png index 338fc4a2e16b..84cda0bebf63 100755 Binary files a/platform/icons/src/vcs/patch.png and b/platform/icons/src/vcs/patch.png differ diff --git a/platform/icons/src/vcs/patch@2x.png b/platform/icons/src/vcs/patch@2x.png index 16df09388284..79ebb7788254 100755 Binary files a/platform/icons/src/vcs/patch@2x.png and b/platform/icons/src/vcs/patch@2x.png differ diff --git a/platform/icons/src/vcs/patch@2x_dark.png b/platform/icons/src/vcs/patch@2x_dark.png index b6088f513b87..f5a1ba334922 100755 Binary files a/platform/icons/src/vcs/patch@2x_dark.png and b/platform/icons/src/vcs/patch@2x_dark.png differ diff --git a/platform/icons/src/vcs/patch_applied.png b/platform/icons/src/vcs/patch_applied.png index 6d09223e4080..0cc7bb02c9bb 100755 Binary files a/platform/icons/src/vcs/patch_applied.png and b/platform/icons/src/vcs/patch_applied.png differ diff --git a/platform/icons/src/vcs/patch_applied@2x.png b/platform/icons/src/vcs/patch_applied@2x.png index 5a0bd7ec17b0..46d748dc8c3b 100755 Binary files a/platform/icons/src/vcs/patch_applied@2x.png and b/platform/icons/src/vcs/patch_applied@2x.png differ diff --git a/platform/icons/src/vcs/patch_applied@2x_dark.png b/platform/icons/src/vcs/patch_applied@2x_dark.png index 8585bc93a851..2f5f0831874b 100755 Binary files a/platform/icons/src/vcs/patch_applied@2x_dark.png and b/platform/icons/src/vcs/patch_applied@2x_dark.png differ diff --git a/platform/icons/src/vcs/patch_applied_dark.png b/platform/icons/src/vcs/patch_applied_dark.png index 214dffb03125..ac28649c0306 100755 Binary files a/platform/icons/src/vcs/patch_applied_dark.png and b/platform/icons/src/vcs/patch_applied_dark.png differ diff --git a/platform/icons/src/vcs/patch_dark.png b/platform/icons/src/vcs/patch_dark.png index ebe6c05e9599..82e29ba61aba 100755 Binary files a/platform/icons/src/vcs/patch_dark.png and b/platform/icons/src/vcs/patch_dark.png differ diff --git a/platform/icons/src/vcs/push.png b/platform/icons/src/vcs/push.png index d1df160ed26a..6b5a293244f0 100755 Binary files a/platform/icons/src/vcs/push.png and b/platform/icons/src/vcs/push.png differ diff --git a/platform/icons/src/vcs/push@2x.png b/platform/icons/src/vcs/push@2x.png index 9f3d47c45811..bbba4dc0656a 100755 Binary files a/platform/icons/src/vcs/push@2x.png and b/platform/icons/src/vcs/push@2x.png differ diff --git a/platform/icons/src/vcs/push@2x_dark.png b/platform/icons/src/vcs/push@2x_dark.png index 97afb58434f8..8c4fd8787b04 100755 Binary files a/platform/icons/src/vcs/push@2x_dark.png and b/platform/icons/src/vcs/push@2x_dark.png differ diff --git a/platform/icons/src/vcs/push_dark.png b/platform/icons/src/vcs/push_dark.png index eb4a78a2b440..90c7a461535b 100755 Binary files a/platform/icons/src/vcs/push_dark.png and b/platform/icons/src/vcs/push_dark.png differ diff --git a/platform/platform-impl/src/com/intellij/internal/inspector/UiInspectorAction.java b/platform/platform-impl/src/com/intellij/internal/inspector/UiInspectorAction.java index 1174a2e37d53..8a8f833f3fd1 100644 --- a/platform/platform-impl/src/com/intellij/internal/inspector/UiInspectorAction.java +++ b/platform/platform-impl/src/com/intellij/internal/inspector/UiInspectorAction.java @@ -1327,7 +1327,7 @@ public class UiInspectorAction extends ToggleAction implements DumbAware { DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; Component rendererComponent = tree.getCellRenderer() - .getTreeCellRendererComponent(tree, node.getUserObject(), tree.getSelectionModel().isPathSelected(path), + .getTreeCellRendererComponent(tree, node, tree.getSelectionModel().isPathSelected(path), tree.isExpanded(path), node.isLeaf(), tree.getRowForPath(path), tree.hasFocus()); clickInfo.add(new PropertyBean(RENDERER_BOUNDS, tree.getPathBounds(path))); clickInfo.addAll(new InspectorTableModel(rendererComponent).myProperties); diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java b/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java index d223f65628a5..9fcb031227a1 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java @@ -186,7 +186,10 @@ public class ApplicationStatisticsPersistenceComponent extends ApplicationStatis JobScheduler.getScheduler().scheduleWithFixedDelay(ApplicationStatisticsPersistenceComponent::persistOpenedProjects, 1, 1, TimeUnit.DAYS); } - private static void persistOpenedProjects() { + /** + * Collects statistics from all opened projects and persists it + */ + public static void persistOpenedProjects() { for (Project project : ProjectManager.getInstance().getOpenProjects()) { UsagesCollector.doPersistProjectUsages(project); } diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/tmp/SendStatisticsAction.java b/platform/platform-impl/src/com/intellij/internal/statistic/tmp/SendStatisticsAction.java index 0b61d2794c4e..5dbf71754fc1 100755 --- a/platform/platform-impl/src/com/intellij/internal/statistic/tmp/SendStatisticsAction.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/tmp/SendStatisticsAction.java @@ -18,6 +18,8 @@ package com.intellij.internal.statistic.tmp; import com.intellij.internal.statistic.StatisticsUploadAssistant; import com.intellij.internal.statistic.connect.StatisticsResult; import com.intellij.internal.statistic.connect.StatisticsService; +import com.intellij.internal.statistic.persistence.ApplicationStatisticsPersistenceComponent; +import com.intellij.internal.statistic.persistence.UsageStatisticsPersistenceComponent; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; @@ -39,9 +41,14 @@ public class SendStatisticsAction extends AnAction { return; } - ProgressManager.getInstance().run(new Task.Backgroundable(project, "Sending Statistics", false) { + ProgressManager.getInstance().run(new Task.Backgroundable(project, "Collecting And Sending Statistics", false) { @Override public void run(@NotNull ProgressIndicator indicator) { + UsageStatisticsPersistenceComponent statisticsPersistenceComponent = UsageStatisticsPersistenceComponent.getInstance(); + boolean sendAllowed = statisticsPersistenceComponent.isAllowed(); + statisticsPersistenceComponent.setAllowed(true); + ApplicationStatisticsPersistenceComponent.persistOpenedProjects(); + statisticsPersistenceComponent.setAllowed(sendAllowed); StatisticsService service = StatisticsUploadAssistant.getStatisticsService(); final StatisticsResult result = service.send(); diff --git a/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml b/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml index ef03e419240c..48a34a79ae3f 100644 --- a/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml @@ -78,7 +78,9 @@ - + diff --git a/platform/platform-resources/src/META-INF/VcsExtensions.xml b/platform/platform-resources/src/META-INF/VcsExtensions.xml index 7b5a8f01920c..769a4b8e56d6 100644 --- a/platform/platform-resources/src/META-INF/VcsExtensions.xml +++ b/platform/platform-resources/src/META-INF/VcsExtensions.xml @@ -40,7 +40,7 @@ - + diff --git a/platform/platform-resources/src/idea/PlatformActions.xml b/platform/platform-resources/src/idea/PlatformActions.xml index 5f42745d5c8f..3da3e00bc5eb 100644 --- a/platform/platform-resources/src/idea/PlatformActions.xml +++ b/platform/platform-resources/src/idea/PlatformActions.xml @@ -788,7 +788,7 @@ - + diff --git a/platform/platform-resources/src/idea/VcsActions.xml b/platform/platform-resources/src/idea/VcsActions.xml index ecf6df0f61e5..bbc474171838 100644 --- a/platform/platform-resources/src/idea/VcsActions.xml +++ b/platform/platform-resources/src/idea/VcsActions.xml @@ -24,9 +24,10 @@ text="Com_pare with Specified Revision..." popup="true"/> - + + @@ -375,6 +376,7 @@ + diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java index 018518bf1e3e..16ae12206f44 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java @@ -300,7 +300,7 @@ public class VfsUtilPerformanceTest extends BareTestFixtureTestCase { }) .assertTiming(); - PlatformTestUtil.startPerformanceTest("many files deletions", 10000, () -> { + PlatformTestUtil.startPerformanceTest("many files deletions", 11000, () -> { assertEquals(N, events.size()); processEvents(events); assertEquals(0, temp.getCachedChildren().size()); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java index 534663d64ce8..9ebcb117aac8 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java @@ -750,7 +750,7 @@ public class VirtualFilePointerTest extends PlatformTestCase { myVirtualFilePointerManager.create(VfsUtilCore.pathToUrl("/a/b/c/d/" + i), disposable, listener); events.add(new VFileCreateEvent(this, temp, "xxx" + i, false, true)); } - PlatformTestUtil.startPerformanceTest("vfp update", 5000, () -> { + PlatformTestUtil.startPerformanceTest("vfp update", 6000, () -> { for (int i=0; i< 100; i++) { // simulate VFS refresh events since launching the actual refresh is too slow myVirtualFilePointerManager.before(events); diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 1d76fc1354d2..24254d529f27 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -558,8 +558,9 @@ diff.verify.iterable.restartRequired=true diff.unimportant.line.char.count=3 diff.unimportant.line.char.count.description=Threshold number of non-space characters to mark line as unimportant diff.unimportant.line.char.count.restartRequired=true -diff.maximum.changes.array.size=10000000 -diff.maximum.changes.array.size.restartRequired=true +diff.delta.threshold.size=20000 +diff.delta.threshold.size.description=Do not try to compare files if difference is bigger than given threshold +diff.delta.threshold.size.restartRequired=true diff.divider.repainting.disable.blitting=true diff.divider.repainting.disable.blitting.description=Fix painting glitch on scrolling in diff - disable BLIT_SCROLL_MODE to force repainting with RepaintManager diff.merge.resolve.conflict.action.visible=true @@ -1166,3 +1167,4 @@ goto.action.skip.tophits.and.options.description=Skip top hits and options in go dsm.show.cycle.count.in.tooltip=false dsm.show.cycle.count.in.tooltip.description=Shows number of cycles found in the tooltip of the Toggle Cycles toolbar button after some calculation time jdk.regex.soe.workaround=true +jdk.regex.soe.workaround.description=In regular expression pattern replace choice \\n|. with . (and DOT_ALL option) to prevent stack overflow during matching diff --git a/platform/util/src/com/intellij/util/diff/Diff.java b/platform/util/src/com/intellij/util/diff/Diff.java index ddac3294c680..92051f57dad9 100644 --- a/platform/util/src/com/intellij/util/diff/Diff.java +++ b/platform/util/src/com/intellij/util/diff/Diff.java @@ -111,8 +111,8 @@ public class Diff { } else { try { - IntLCS intLCS = new IntLCS(discarded[0], discarded[1]); - intLCS.execute(); + MyersLCS intLCS = new MyersLCS(discarded[0], discarded[1]); + intLCS.executeWithThreshold(); changes = intLCS.getChanges(); } catch (FilesTooBigForDiffException e) { diff --git a/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java b/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java index 01e3712c4086..e535652fca11 100644 --- a/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java +++ b/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java @@ -8,8 +8,7 @@ import com.intellij.openapi.util.registry.Registry; * Time: 10:30 PM */ public class FilesTooBigForDiffException extends Exception { - // Limit for memory consumption in IntLCS algorithm. ~ 2000 changed lines, 50Mb memory - public static final int MAX_BUFFER_LEN = Registry.intValue("diff.maximum.changes.array.size"); + public static final int DELTA_THRESHOLD_SIZE = Registry.intValue("diff.delta.threshold.size"); // Do not try to compare two lines by-word after this much fails. public static final int MAX_BAD_LINES = 3; diff --git a/platform/util/src/com/intellij/util/diff/IntLCS.java b/platform/util/src/com/intellij/util/diff/IntLCS.java deleted file mode 100644 index 7fc7162f27ed..000000000000 --- a/platform/util/src/com/intellij/util/diff/IntLCS.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2000-2009 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.util.diff; - -import java.util.Arrays; -import java.util.BitSet; - -/** - * @author dyoma - */ -class IntLCS { - private final int[] myFirst; - private final int[] mySecond; - - private final int myStart1; - private final int myStart2; - - private final LinkedDiffPaths myPathsMatrix; - private final int[] myPrevPathKey; - private int[] myPrevEnds; - private int[] myCurrentEnds; - private final int myMaxX; - private final int myMaxY; - - private final BitSet myChanges1; - private final BitSet myChanges2; - - public IntLCS(int[] first, int[] second) { - this(first, second, 0, first.length, 0, second.length, new BitSet(first.length), new BitSet(second.length)); - } - - public IntLCS(int[] first, int[] second, int start1, int count1, int start2, int count2, BitSet changes1, BitSet changes2) { - myFirst = first; - mySecond = second; - myStart1 = start1; - myStart2 = start2; - myMaxX = count1; - myMaxY = count2; - - myChanges1 = changes1; - myChanges2 = changes2; - - myPathsMatrix = new LinkedDiffPaths(myMaxX, myMaxY); - myPrevPathKey = new int[myMaxX + myMaxY + 1]; - Arrays.fill(myPrevPathKey, -1); - myPrevEnds = new int[myMaxX + myMaxY + 1]; - myCurrentEnds = new int[myMaxX + myMaxY + 1]; - } - - public int execute() throws FilesTooBigForDiffException { - for (int d =0; d <= myMaxX + myMaxY; d++) { - int minDiag = -calcBound(myMaxY, d); - int maxDiag = calcBound(myMaxX, d); - if (d != 0) - System.arraycopy(myPrevEnds, minDiag + myMaxY, myCurrentEnds, minDiag + myMaxY, maxDiag - minDiag); - else { - int end = skipEquals(0, 0); - if (end > 0) { - int xy = (end) - 1; - myPrevPathKey[myMaxY] = myPathsMatrix.encodeStep(xy, xy, end, false, -1); - } - if (myMaxX == myMaxY && end == myMaxX) return 0; - myPrevEnds[myMaxY] = end; - continue; - } - for (int k = minDiag; k <= maxDiag; k += 2) { - int end; - if (k == -d) { - int prevEndV = myPrevEnds[k + 1 + myMaxY]; - int vertical = findDiagonalEnd(k + 1, prevEndV, true); - end = encodeStep(prevEndV, vertical, k, true); - } else if (k == d) { - int prevEndH = myPrevEnds[k - 1 + myMaxY]; - int horisontal = findDiagonalEnd(k - 1, prevEndH, false); - end = encodeStep(prevEndH, horisontal, k, false); - } else { - int prevEndH = myPrevEnds[k - 1 + myMaxY]; - int prevEndV = myPrevEnds[k + 1 + myMaxY]; - if (prevEndH+1 > prevEndV) { - int horisontal = findDiagonalEnd(k - 1, prevEndH, false); - end = encodeStep(prevEndH, horisontal, k, false); - } else { - int vertical = findDiagonalEnd(k + 1, prevEndV, true); - end = encodeStep(prevEndV, vertical, k, true); - } - } - myCurrentEnds[k + myMaxY] = end; - if (k == myMaxX - myMaxY && end == myMaxX) { - myPathsMatrix.applyChanges(myStart1, myStart2, myChanges1, myChanges2); - return d; - } - } - int[] temps = myCurrentEnds; - myCurrentEnds = myPrevEnds; - myPrevEnds = temps; - } - throw new RuntimeException(); - } - - public BitSet[] getChanges() { - return new BitSet[]{myChanges1, myChanges2}; - } - - private int findDiagonalEnd(int prevDiagonal, int prevEnd, boolean isVertical) { - int x = prevEnd; - int y = x - prevDiagonal; - if (isVertical) y++; - else x++; - return skipEquals(x, y); - } - - private int encodeStep(int prevEnd, int diagLength, int tDiagonal, boolean afterVertical) throws FilesTooBigForDiffException { - int end = prevEnd + diagLength; - int prevDiagonal = tDiagonal + myMaxY; - if (!afterVertical) end++; - if (afterVertical) prevDiagonal++; - else prevDiagonal--; - int x = end - 1; - int y = x - tDiagonal; - if (x == -1 || y == -1 || x >= myMaxX || y >= myMaxY) return end; - myPrevPathKey[tDiagonal + myMaxY] = myPathsMatrix.encodeStep(x, y, diagLength, afterVertical, myPrevPathKey[prevDiagonal]); - return end; - } - - private int calcBound(int bound, int d) { - return (d <= bound) ? d : 2 * bound - d; - } - - private int skipEquals(int x, int y) { - int skipped = 0; - while (x < myMaxX && y < myMaxY && myFirst[myStart1 + x] == mySecond[myStart2 + y]) { - skipped += 1; - x++; - y++; - } - return skipped; - } -} diff --git a/platform/util/src/com/intellij/util/diff/LinkedDiffPaths.java b/platform/util/src/com/intellij/util/diff/LinkedDiffPaths.java deleted file mode 100644 index bc7582077940..000000000000 --- a/platform/util/src/com/intellij/util/diff/LinkedDiffPaths.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2000-2009 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.util.diff; - -import java.util.BitSet; - -/** - * @author dyoma - */ -final class LinkedDiffPaths { - - private int[] mySteps = new int[10]; - private int[] myPrevSteps = new int[10]; - private int myPosition = 0; - private final int myMaxX; - private final int myMaxY; - private int myCornerIndex = -1; - private static final int VERTICAL_DIRECTION_FLAG = 1 << 31; - private static final int DISTANCE_MASK = ~VERTICAL_DIRECTION_FLAG; - - public LinkedDiffPaths(int maxX, int maxY) { - myMaxX = maxX; - myMaxY = maxY; - } - - public void applyChanges(final int start1, final int start2, final BitSet changes1, final BitSet changes2) { - decodePath(new LCSBuilder() { - int x = myMaxX; - int y = myMaxY; - - @Override - public void addEqual(int length) { - x -= length; - y -= length; - } - - @Override - public void addChange(int first, int second) { - if (first > 0) { - changes1.set(start1 + x - first, start1 + x); - x -= first; - } - if (second > 0) { - changes2.set(start2 + y - second, start2 + y); - y -= second; - } - } - }); - } - - /** - * Path is decoded in reverse order (from the last change to the first) - */ - public Builder decodePath(Builder builder) { - Decoder decoder = new Decoder(getXSize(), getYSize(), builder); - int index = myCornerIndex; - while (index != -1) { - int encodedStep = mySteps[index]; - decoder.decode(encodedStep); - index = myPrevSteps[index]; - } - decoder.beforeFinish(); - return builder; - } - - public int getXSize() { - return myMaxX; - } - - public int getYSize() { - return myMaxY; - } - - public int encodeStep(int x, int y, int diagLength, boolean afterVertical, int prevIndex) throws FilesTooBigForDiffException { - int encodedPath = diagLength; - if (afterVertical) encodedPath |= VERTICAL_DIRECTION_FLAG; - int position = incPosition(); - - myPrevSteps[position] = prevIndex; - mySteps[position] = encodedPath; - if (x == myMaxX - 1 && y == myMaxY - 1) myCornerIndex = position; - return position; - } - - private int incPosition() throws FilesTooBigForDiffException { - int length = myPrevSteps.length; - if (myPosition == length - 1) { - myPrevSteps = copy(length, myPrevSteps); - mySteps = copy(length, mySteps); - } - myPosition++; - return myPosition; - } - - private int[] copy(int length, int[] prevArray) throws FilesTooBigForDiffException { - if (length * 2 >= FilesTooBigForDiffException.MAX_BUFFER_LEN) { - throw new FilesTooBigForDiffException(FilesTooBigForDiffException.MAX_BUFFER_LEN); - } - int[] array = new int[length * 2]; - System.arraycopy(prevArray, 0, array, 0, length); - return array; - } - - class Decoder { - private final LCSBuilder builder; - private int x; - private int y; - private int dx = 0; - private int dy = 0; - - public Decoder(int x, int y, LCSBuilder builder) { - this.x = x; - this.y = y; - this.builder = builder; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - - public void decode(int encodedStep) { - int diagDist = encodedStep & DISTANCE_MASK; - if (diagDist != 0) { - if (dx != 0 || dy != 0) { - builder.addChange(dx, dy); - dx = 0; - dy = 0; - } - builder.addEqual(diagDist); - } - x -= diagDist; - y -= diagDist; - boolean verticalStep = (encodedStep & VERTICAL_DIRECTION_FLAG) != 0; - if (verticalStep) { - y--; - dy++; - } else { - x--; - dx++; - } - } - - public void beforeFinish() { - dx += x; - dy += y; - if (dx != 0 || dy != 0) builder.addChange(dx, dy); - } - } -} diff --git a/platform/util/src/com/intellij/util/diff/MyersLCS.java b/platform/util/src/com/intellij/util/diff/MyersLCS.java new file mode 100644 index 000000000000..11534b8e84ce --- /dev/null +++ b/platform/util/src/com/intellij/util/diff/MyersLCS.java @@ -0,0 +1,224 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.diff; + +import java.util.BitSet; + +/** + * Algorithm for finding the longest common subsequence of two strings + * Based on E.W. Myers / An O(ND) Difference Algorithm and Its Variations / 1986 + * O(ND) runtime, O(N) memory + *

+ * Created by Anton Bannykh + */ +class MyersLCS { + private final int[] myFirst; + private final int[] mySecond; + + private final int myStart1; + private final int myStart2; + private final int myCount1; + private final int myCount2; + + private final BitSet myChanges1; + private final BitSet myChanges2; + + private final int[] VForward; + private final int[] VBackward; + + public MyersLCS(int[] first, int[] second) { + this(first, second, 0, first.length, 0, second.length, new BitSet(first.length), new BitSet(second.length)); + } + + public MyersLCS(int[] first, int[] second, int start1, int count1, int start2, int count2, BitSet changes1, BitSet changes2) { + myFirst = first; + mySecond = second; + myStart1 = start1; + myStart2 = start2; + myCount1 = count1; + myCount2 = count2; + + myChanges1 = changes1; + myChanges2 = changes2; + + myChanges1.set(myStart1, myStart1 + myCount1); + myChanges2.set(myStart2, myStart2 + myCount2); + + final int totalSequenceLength = myCount1 + myCount2; + VForward = new int[totalSequenceLength + 1]; + VBackward = new int[totalSequenceLength + 1]; + } + + /** + * Runs O(ND) Myers algorithm where D is bound by A + B * sqrt(N) + *

+ * Under certains assumptions about the distribution of the elements of the sequences the expected + * running time of the myers algorithm is O(N + D^2). Thus under given constraints it reduces to O(N). + */ + public void executeLinear() { + try { + int threshold = 20000 + 10 * (int)Math.sqrt(myCount1 + myCount2); + execute(threshold, false); + } + catch (FilesTooBigForDiffException e) { + throw new IllegalStateException(e); // should not happen + } + } + + public void execute() { + try { + execute(myCount1 + myCount2, false); + } + catch (FilesTooBigForDiffException e) { + throw new IllegalStateException(e); // should not happen + } + } + + public void executeWithThreshold() throws FilesTooBigForDiffException { + int threshold = Math.max(20000 + 10 * (int)Math.sqrt(myCount1 + myCount2), + FilesTooBigForDiffException.DELTA_THRESHOLD_SIZE); + execute(threshold, true); + } + + private void execute(int threshold, boolean throwException) throws FilesTooBigForDiffException { + if (myCount1 == 0 || myCount2 == 0) return; + execute(0, myCount1, 0, myCount2, Math.min(threshold, myCount1 + myCount2), throwException); + } + + //LCS( old[oldStart, oldEnd), new[newStart, newEnd) ) + private void execute(int oldStart, int oldEnd, int newStart, int newEnd, int differenceEstimate, + boolean throwException) throws FilesTooBigForDiffException { + assert oldStart <= oldEnd && newStart <= newEnd; + if (oldStart < oldEnd && newStart < newEnd) { + final int oldLength = oldEnd - oldStart; + final int newLength = newEnd - newStart; + VForward[newLength + 1] = 0; + VBackward[newLength + 1] = 0; + final int halfD = (differenceEstimate + 1) / 2; + int xx, kk, td; + xx = kk = td = -1; + + loop: + for (int d = 0; d <= halfD; ++d) { + final int L = newLength + Math.max(-d, -newLength + ((d ^ newLength) & 1)); + final int R = newLength + Math.min(d, oldLength - ((d ^ oldLength) & 1)); + for (int k = L; k <= R; k += 2) { + int x = k == L || k != R && VForward[k - 1] < VForward[k + 1] ? VForward[k + 1] : VForward[k - 1] + 1; + int y = x - k + newLength; + x += commonSubsequenceLengthForward(oldStart + x, newStart + y, + Math.min(oldEnd - oldStart - x, newEnd - newStart - y)); + VForward[k] = x; + } + + if ((oldLength - newLength) % 2 != 0) { + for (int k = L; k <= R; k += 2) { + if (oldLength - (d - 1) <= k && k <= oldLength + (d - 1)) { + if (VForward[k] + VBackward[newLength + oldLength - k] >= oldLength) { + xx = VForward[k]; + kk = k; + td = 2 * d - 1; + break loop; + } + } + } + } + + for (int k = L; k <= R; k += 2) { + int x = k == L || k != R && VBackward[k - 1] < VBackward[k + 1] ? VBackward[k + 1] : VBackward[k - 1] + 1; + int y = x - k + newLength; + x += commonSubsequenceLengthBackward(oldEnd - 1 - x, newEnd - 1 - y, + Math.min(oldEnd - oldStart - x, newEnd - newStart - y)); + VBackward[k] = x; + } + + if ((oldLength - newLength) % 2 == 0) { + for (int k = L; k <= R; k += 2) { + if (oldLength - d <= k && k <= oldLength + d) { + if (VForward[oldLength + newLength - k] + VBackward[k] >= oldLength) { + xx = oldLength - VBackward[k]; + kk = oldLength + newLength - k; + td = 2 * d; + break loop; + } + } + } + } + } + + if (td > 1) { + final int yy = xx - kk + newLength; + final int oldDiff = (td + 1) / 2; + if (0 < xx && 0 < yy) execute(oldStart, oldStart + xx, newStart, newStart + yy, oldDiff, throwException); + if (oldStart + xx < oldEnd && newStart + yy < newEnd) execute(oldStart + xx, oldEnd, newStart + yy, newEnd, td - oldDiff, throwException); + } + else if (td >= 0) { + int x = oldStart; + int y = newStart; + while (x < oldEnd && y < newEnd) { + final int commonLength = commonSubsequenceLengthForward(x, y, Math.min(oldEnd - x, newEnd - y)); + if (commonLength > 0) { + addUnchanged(x, y, commonLength); + x += commonLength; + y += commonLength; + } + else if (oldEnd - oldStart > newEnd - newStart) { + ++x; + } + else { + ++y; + } + } + } + else { + //The difference is more than the given estimate + if (throwException) throw new FilesTooBigForDiffException(0); + } + } + } + + private void addUnchanged(int start1, int start2, int count) { + myChanges1.set(myStart1 + start1, myStart1 + start1 + count, false); + myChanges2.set(myStart2 + start2, myStart2 + start2 + count, false); + } + + private int commonSubsequenceLengthForward(int oldIndex, int newIndex, int maxLength) { + int x = oldIndex; + int y = newIndex; + + maxLength = Math.min(maxLength, Math.min(myCount1 - oldIndex, myCount2 - newIndex)); + while (x - oldIndex < maxLength && myFirst[myStart1 + x] == mySecond[myStart2 + y]) { + ++x; + ++y; + } + return x - oldIndex; + } + + private int commonSubsequenceLengthBackward(int oldIndex, int newIndex, int maxLength) { + int x = oldIndex; + int y = newIndex; + + maxLength = Math.min(maxLength, Math.min(oldIndex, newIndex) + 1); + while (oldIndex - x < maxLength && myFirst[myStart1 + x] == mySecond[myStart2 + y]) { + --x; + --y; + } + return oldIndex - x; + } + + public BitSet[] getChanges() { + return new BitSet[]{myChanges1, myChanges2}; + } +} diff --git a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java index 0f885eca41cf..9a26a304d017 100644 --- a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java +++ b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java @@ -86,8 +86,8 @@ class PatienceIntLCS { if (matching == null) { if (thresholdCheckCounter >= 0) checkReduction(count1, count2); - IntLCS intLCS = new IntLCS(myFirst, mySecond, start1, count1, start2, count2, myChanges1, myChanges2); - intLCS.execute(); + MyersLCS intLCS = new MyersLCS(myFirst, mySecond, start1, count1, start2, count2, myChanges1, myChanges2); + intLCS.executeLinear(); } else { int s1, s2, c1, c2; diff --git a/platform/util/src/com/intellij/util/lang/ClassPath.java b/platform/util/src/com/intellij/util/lang/ClassPath.java index 7a78abc05712..67419b928d3d 100644 --- a/platform/util/src/com/intellij/util/lang/ClassPath.java +++ b/platform/util/src/com/intellij/util/lang/ClassPath.java @@ -76,7 +76,10 @@ public class ClassPath { push(urls); } - /** @deprecated to be removed in IDEA 15 */ + /** + * @deprecated Adding additional urls to classpath at runtime could lead to hard-to-debug errors + */ + @Deprecated void addURL(URL url) { push(Collections.singletonList(url)); } @@ -269,8 +272,21 @@ public class ClassPath { if (myCanUseCache && myAllUrlsWereProcessed) { loaders = new SmartList(); myCache.iterateLoaders(name, ourLoaderCollector, loaders, this); - if (!name.endsWith("/")) { - myCache.iterateLoaders(name.concat("/"), ourLoaderCollector, loaders, this); + + if (!name.endsWith("/")) { // avoid loader duplicates when we are requesting nondirectory name + boolean nameIsDirectory = name.endsWith("/"); + Collection loadersSet = nameIsDirectory ? new SmartList() : new LinkedHashSet(); + myCache.iterateLoaders(name, ourLoaderCollector, loadersSet, this); + + if (!nameIsDirectory) { + myCache.iterateLoaders(name.concat("/"), ourLoaderCollector, loadersSet, this); + } + + if (nameIsDirectory) { + loaders = (List)loadersSet; + } else { + loaders = new ArrayList(loadersSet); + } } } @@ -341,9 +357,9 @@ public class ClassPath { } } - private static class LoaderCollector extends ClasspathCache.LoaderIterator, Object> { + private static class LoaderCollector extends ClasspathCache.LoaderIterator, Object> { @Override - Object process(Loader loader, List parameter, Object parameter2) { + Object process(Loader loader, Collection parameter, Object parameter2) { parameter.add(loader); return null; } diff --git a/platform/util/src/com/intellij/util/lang/JarLoader.java b/platform/util/src/com/intellij/util/lang/JarLoader.java index d95963035c3b..9c54f0ba0a1f 100644 --- a/platform/util/src/com/intellij/util/lang/JarLoader.java +++ b/platform/util/src/com/intellij/util/lang/JarLoader.java @@ -19,12 +19,11 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.reference.SoftReference; +import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.net.URL; import java.util.*; import java.util.jar.Attributes; @@ -131,7 +130,7 @@ class JarLoader extends Loader { try { ZipEntry entry = zipFile.getEntry(name); if (entry != null) { - return MemoryResource.load(getBaseURL(), zipFile, entry, myAttributes); + return new MyResource(getBaseURL(), entry); } } finally { @@ -144,6 +143,49 @@ class JarLoader extends Loader { return null; } + + private class MyResource extends Resource { + private final URL myUrl; + private final ZipEntry myEntry; + + public MyResource(URL url, ZipEntry entry) throws IOException { + myUrl = new URL(url, entry.getName()); + myEntry = entry; + } + + @Override + public URL getURL() { + return myUrl; + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(getBytes()); + } + + @Override + public byte[] getBytes() throws IOException { + byte[] result = ArrayUtil.EMPTY_BYTE_ARRAY; + try { + ZipFile file = getZipFile(); + try { + result = FileUtil.loadBytes(file.getInputStream(myEntry), (int)myEntry.getSize()); + } finally { + releaseZipFile(file); + } + } + catch (Exception e) { + error("file: " + myCanonicalFile, e); + } + + return result; + } + + @Override + public String getValue(Attribute key) { + return myAttributes != null ? myAttributes.get(key) : null; + } + } protected void error(String message, Throwable t) { Logger.getInstance(JarLoader.class).error(message, t); diff --git a/platform/util/src/com/intellij/util/lang/UrlClassLoader.java b/platform/util/src/com/intellij/util/lang/UrlClassLoader.java index 20215a5f6178..5f8e2a8ed15f 100644 --- a/platform/util/src/com/intellij/util/lang/UrlClassLoader.java +++ b/platform/util/src/com/intellij/util/lang/UrlClassLoader.java @@ -195,8 +195,12 @@ public class UrlClassLoader extends ClassLoader { } } - /** @deprecated to be removed in IDEA 15 */ + /** + * @deprecated Adding additional urls to classloader at runtime could lead to hard-to-debug errors + * Note: Used via reflection because of classLoaders incompatibility + */ @SuppressWarnings({"unused", "deprecation"}) + @Deprecated public void addURL(URL url) { getClassPath().addURL(url); myURLs.add(url); diff --git a/platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java b/platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java new file mode 100644 index 000000000000..f19d756c7b6a --- /dev/null +++ b/platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.diff; + +import junit.framework.TestCase; +import org.jetbrains.annotations.NotNull; + +import java.util.BitSet; +import java.util.Random; + +public class IntLCSAutoTest extends TestCase { + private final Random RNG = new Random(); + + private static final int ITERATIONS = 1000; + private static final int MAX_LENGTH = 300; + private static final int CHAR_COUNT = 20; + + private enum Type {MyersLCS, PatienceLCS} + + public void testMyersLCS() throws Exception { + for (int i = 0; i < ITERATIONS; i++) { + doTestLCS(MAX_LENGTH, CHAR_COUNT, Type.MyersLCS); + } + } + + public void testPatienceLCS() throws Exception { + for (int i = 0; i < ITERATIONS; i++) { + doTestLCS(MAX_LENGTH, CHAR_COUNT, Type.PatienceLCS); + } + } + + private void doTestLCS(int maxLength, int charCount, @NotNull Type type) throws FilesTooBigForDiffException { + int[] sequence1 = generateSequence(maxLength, charCount); + int[] sequence2 = generateSequence(maxLength, charCount); + + int start1 = RNG.nextInt(sequence1.length); + int start2 = RNG.nextInt(sequence2.length); + int count1 = RNG.nextInt(sequence1.length - start1); + int count2 = RNG.nextInt(sequence2.length - start2); + + BitSet changes1 = new BitSet(sequence1.length); + BitSet changes2 = new BitSet(sequence2.length); + + switch (type) { + case MyersLCS: + MyersLCS myersLCS = new MyersLCS(sequence1, sequence2, start1, count1, start2, count2, changes1, changes2); + myersLCS.execute(); + break; + case PatienceLCS: + PatienceIntLCS patienceLCS = new PatienceIntLCS(sequence1, sequence2, start1, count1, start2, count2, changes1, changes2); + patienceLCS.execute(); + break; + } + + verifyLCS(sequence1, sequence2, start1, count1, start2, count2, changes1, changes2); + } + + public static void verifyLCS(@NotNull int[] sequence1, @NotNull int[] sequence2, + @NotNull BitSet changes1, @NotNull BitSet changes2) { + verifyLCS(sequence1, sequence2, 0, sequence1.length, 0, sequence2.length, changes1, changes2); + } + + private static void verifyLCS(@NotNull int[] sequence1, @NotNull int[] sequence2, + int start1, int count1, int start2, int count2, + @NotNull BitSet changes1, @NotNull BitSet changes2) { + int index1 = changes1.nextClearBit(start1); + int index2 = changes2.nextClearBit(start2); + + while (index1 < start1 + count1 || index2 < start2 + count2) { + assertTrue(index1 < start1 + count1); + assertTrue(index2 < start2 + count2); + + assertEquals(sequence1[index1], sequence2[index2]); + + index1 = changes1.nextClearBit(index1 + 1); + index2 = changes2.nextClearBit(index2 + 1); + } + assertTrue(index1 >= start1 + count1 && index2 >= start2 + count2); + } + + @NotNull + private int[] generateSequence(int maxLength, int charCount) { + int[] result = new int[RNG.nextInt(maxLength / 2) + maxLength / 2]; + for (int i = 0; i < result.length; i++) { + result[i] = RNG.nextInt(charCount); + } + return result; + } +} diff --git a/platform/util/testSrc/com/intellij/util/diff/IntLCSNewTest.java b/platform/util/testSrc/com/intellij/util/diff/IntLCSNewTest.java index 31fd1ad0cbcd..647243c5602e 100644 --- a/platform/util/testSrc/com/intellij/util/diff/IntLCSNewTest.java +++ b/platform/util/testSrc/com/intellij/util/diff/IntLCSNewTest.java @@ -71,9 +71,9 @@ public class IntLCSNewTest extends TestCase { } private static BitSet[] buildChange(int[] first, int[] second) throws FilesTooBigForDiffException { - IntLCS intLCS = new IntLCS(first, second); - intLCS.execute(); - return intLCS.getChanges(); + MyersLCS myersLCS = new MyersLCS(first, second); + myersLCS.execute(); + return myersLCS.getChanges(); } private static void checkChange(BitSet[] change, int[] expected1, int[] expected2) { diff --git a/platform/util/testSrc/com/intellij/util/diff/IntLCSTest.java b/platform/util/testSrc/com/intellij/util/diff/IntLCSTest.java index 87d1fad58435..aa8ee8d2058e 100644 --- a/platform/util/testSrc/com/intellij/util/diff/IntLCSTest.java +++ b/platform/util/testSrc/com/intellij/util/diff/IntLCSTest.java @@ -18,6 +18,8 @@ package com.intellij.util.diff; import junit.framework.TestCase; +import java.util.BitSet; + /** * @author dyoma */ @@ -48,7 +50,7 @@ public class IntLCSTest extends TestCase { } public void testOneTail() throws FilesTooBigForDiffException { - assertEquals(1, new IntLCS(new int[]{1, 2}, new int[]{1, 2, 3}).execute()); + assertEquals(1, countChanges(new int[]{1, 2}, new int[]{1, 2, 3})); } public void testSingleMiddle() throws FilesTooBigForDiffException { @@ -58,13 +60,14 @@ public class IntLCSTest extends TestCase { } public void testAbsolutelyDifferent() throws FilesTooBigForDiffException { - assertEquals(4, new IntLCS(new int[]{1, 2}, new int[]{3, 4}).execute()); - assertEquals(6, new IntLCS(new int[]{1, 2, 3}, new int[]{4, 5, 6}).execute()); + assertEquals(4, countChanges(new int[]{1, 2}, new int[]{3, 4})); + assertEquals(6, countChanges(new int[]{1, 2, 3}, new int[]{4, 5, 6})); } - private Diff.Change buildChange(int[] first, int[] second, int expectedNonDiags) throws FilesTooBigForDiffException { - IntLCS intLCS = new IntLCS(first, second); - assertEquals(expectedNonDiags, intLCS.execute()); + private static Diff.Change buildChange(int[] first, int[] second, int expectedNonDiags) throws FilesTooBigForDiffException { + assertEquals(expectedNonDiags, countChanges(first, second)); + MyersLCS intLCS = new MyersLCS(first, second); + intLCS.execute(); Reindexer reindexer = new Reindexer(); reindexer.idInit(first.length, second.length); Diff.ChangeBuilder builder = new Diff.ChangeBuilder(0); @@ -85,4 +88,11 @@ public class IntLCSTest extends TestCase { checkChange(change, line0, line1, inserted, deleted); assertNull("Expected last change", change.link); } + + private static int countChanges(int[] first, int[] second) throws FilesTooBigForDiffException { + MyersLCS lcs = new MyersLCS(first, second); + lcs.execute(); + BitSet[] changes = lcs.getChanges(); + return changes[0].cardinality() + changes[1].cardinality(); + } } diff --git a/platform/util/testSrc/com/intellij/util/diff/LinkedDiffPathsTest.java b/platform/util/testSrc/com/intellij/util/diff/LinkedDiffPathsTest.java deleted file mode 100644 index 0f9300ab3d8c..000000000000 --- a/platform/util/testSrc/com/intellij/util/diff/LinkedDiffPathsTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2000-2009 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.util.diff; - -import junit.framework.TestCase; - -import java.util.BitSet; - -/** - * @author dyoma - */ -public class LinkedDiffPathsTest extends TestCase { - protected LinkedDiffPaths createPaths(int maxX, int maxY) { - return new LinkedDiffPaths(maxX, maxY); - } - - public void testOneDeleteAtferEnd() throws FilesTooBigForDiffException { - LinkedDiffPaths paths = createPaths(2, 3); - int key = paths.encodeStep(1, 1, 2, false, -1); - paths.encodeStep(1, 2, 0, true, key); - Diff.Change change = decode(paths); - IntLCSTest.checkLastChange(change, 2, 2, 1, 0); - } - - public void testOneInsertedAtBegging() throws FilesTooBigForDiffException { - LinkedDiffPaths paths = createPaths(3, 2); - paths.encodeStep(2, 1, 2, false, -1); - Diff.Change change = decode(paths); - IntLCSTest.checkLastChange(change, 0, 0, 0, 1); - } - - public void testSingleMiddleChange() throws FilesTooBigForDiffException { - LinkedDiffPaths paths = createPaths(3, 3); - int key = paths.encodeStep(0, 0, 1, true, -1); - key = paths.encodeStep(1, 0, 0, false, key); - paths.encodeStep(2, 2, 1, true, key); - IntLCSTest.checkLastChange(decode(paths), 1, 1, 1, 1); - } - - public void testSingleChangeAtEnd() throws FilesTooBigForDiffException { - LinkedDiffPaths paths = createPaths(2, 2); - int key = paths.encodeStep(0, 0, 1, false, -1); - key = paths.encodeStep(0, 1, 0, true, key); - paths.encodeStep(1, 1, 0, false, key); - IntLCSTest.checkLastChange(decode(paths), 1, 1, 1, 1); - } - - public void testNotSquareChangeAtEnd() throws FilesTooBigForDiffException { - LinkedDiffPaths paths = createPaths(2, 3); - int key = paths.encodeStep(0, 0, 1, false, -1); - key = paths.encodeStep(0, 1, 0, true, key); - key = paths.encodeStep(0, 2, 0, true, key); - paths.encodeStep(1, 2, 0, false, key); - IntLCSTest.checkLastChange(decode(paths), 1, 1, 2, 1); - } - - private Diff.Change decode(LinkedDiffPaths paths) { - BitSet[] changes = new BitSet[]{new BitSet(), new BitSet()}; - paths.applyChanges(0, 0, changes[0], changes[1]); - Reindexer reindexer = new Reindexer(); - reindexer.idInit(paths.getXSize(), paths.getYSize()); - Diff.ChangeBuilder builder = new Diff.ChangeBuilder(0); - reindexer.reindex(changes, builder); - return builder.getFirstChange(); - } -} diff --git a/platform/util/testSrc/com/intellij/util/diff/ReindexerNewTest.java b/platform/util/testSrc/com/intellij/util/diff/ReindexerNewTest.java index c9fe6fbf9463..764515e43c38 100644 --- a/platform/util/testSrc/com/intellij/util/diff/ReindexerNewTest.java +++ b/platform/util/testSrc/com/intellij/util/diff/ReindexerNewTest.java @@ -77,7 +77,7 @@ public class ReindexerNewTest extends TestCase { } public static void checkCase(int[] ints1, int[] ints2) throws FilesTooBigForDiffException { - checkCase(ints1, ints2, null); + checkCase(ints1, ints2, ""); } public static void checkCase(int[] ints1, int[] ints2, String message) throws FilesTooBigForDiffException { @@ -102,24 +102,12 @@ public class ReindexerNewTest extends TestCase { } }; - IntLCS lcs = new IntLCS(ints1, ints2); - lcs.execute(); - BitSet[] expectedChanges = lcs.getChanges(); - Reindexer reindexer = new Reindexer(); int[][] discarded = reindexer.discardUnique(ints1, ints2); - lcs = new IntLCS(discarded[0], discarded[1]); + MyersLCS lcs = new MyersLCS(discarded[0], discarded[1]); lcs.execute(); - BitSet[] changes = lcs.getChanges(); - reindexer.reindex(changes, builder); + reindexer.reindex(lcs.getChanges(), builder); - if (message != null) { - assertEquals(message, expectedChanges[0], reindexChanges[0]); - assertEquals(message, expectedChanges[1], reindexChanges[1]); - } - else { - assertEquals(expectedChanges[0], reindexChanges[0]); - assertEquals(expectedChanges[1], reindexChanges[1]); - } + IntLCSAutoTest.verifyLCS(ints1, ints2, reindexChanges[0], reindexChanges[1]); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffWithLocalAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffWithLocalAction.java index ae56ad00304e..1faa3958b2ee 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffWithLocalAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffWithLocalAction.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vcs.changes.actions; +import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -24,6 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsDataKeys; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.committed.CommittedChangesBrowserUseCase; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,12 +34,17 @@ import java.util.List; import static com.intellij.openapi.vcs.changes.actions.diff.ShowDiffAction.showDiffForChange; -/** - * @author yole - */ public class ShowDiffWithLocalAction extends AnAction implements DumbAware { + private final boolean myBeforeWithLocal; + public ShowDiffWithLocalAction() { - ActionUtil.copyFrom(this, "Vcs.ShowDiffWithLocal"); + this(false); + getTemplatePresentation().setIcon(AllIcons.Actions.DiffWithCurrent); + } + + public ShowDiffWithLocalAction(boolean beforeWithLocal) { + myBeforeWithLocal = beforeWithLocal; + ActionUtil.copyFrom(this, beforeWithLocal ? "Vcs.ShowDiffWithLocal.Before" : "Vcs.ShowDiffWithLocal"); } public void actionPerformed(AnActionEvent e) { @@ -49,9 +56,9 @@ public class ShowDiffWithLocalAction extends AnAction implements DumbAware { List changesToLocal = new ArrayList<>(); for (int i = 0; i < selection.getChanges().size(); i++) { if (i == selection.getIndex()) index = changesToLocal.size(); - ContentRevision afterRevision = selection.getChanges().get(i).getAfterRevision(); - if (afterRevision != null && isValidAfterRevision(afterRevision)) { - changesToLocal.add(new Change(afterRevision, getCurrentRevision(afterRevision, e))); + Change change = getChangeWithLocal(selection.getChanges().get(i)); + if (change != null) { + changesToLocal.add(change); } } @@ -60,29 +67,30 @@ public class ShowDiffWithLocalAction extends AnAction implements DumbAware { } } - @NotNull - protected ContentRevision getCurrentRevision(@NotNull ContentRevision afterRevision, @NotNull AnActionEvent e) { - return CurrentContentRevision.create(afterRevision.getFile()); - } - public void update(final AnActionEvent e) { Project project = e.getData(CommonDataKeys.PROJECT); ChangesSelection selection = e.getData(VcsDataKeys.CHANGES_SELECTION); boolean isInAir = CommittedChangesBrowserUseCase.IN_AIR.equals(CommittedChangesBrowserUseCase.DATA_KEY.getData(e.getDataContext())); + boolean isToolbar = "ChangesBrowser".equals(e.getPlace()); - e.getPresentation().setEnabled(project != null && selection != null && !isInAir && anyHasAfterRevision(selection.getChanges())); + e.getPresentation().setEnabled(project != null && !isToolbar && selection != null && !isInAir && canShowDiff(selection.getChanges())); + e.getPresentation().setVisible(!isToolbar); } - private static boolean isValidAfterRevision(@Nullable final ContentRevision afterRevision) { - return afterRevision != null && !afterRevision.getFile().isNonLocal() && !afterRevision.getFile().isDirectory(); + @Nullable + private Change getChangeWithLocal(@NotNull Change c) { + ContentRevision revision = myBeforeWithLocal ? c.getBeforeRevision() : c.getAfterRevision(); + if (!isValidRevision(revision)) return null; + + ContentRevision contentRevision = CurrentContentRevision.create(revision.getFile()); + return new Change(revision, contentRevision); } - private static boolean anyHasAfterRevision(@NotNull final List changes) { - for (Change c : changes) { - if (isValidAfterRevision(c.getAfterRevision())) { - return true; - } - } - return false; + private boolean canShowDiff(@NotNull List changes) { + return ContainerUtil.exists(changes, c -> getChangeWithLocal(c) != null); + } + + private static boolean isValidRevision(@Nullable ContentRevision revision) { + return revision != null && !revision.getFile().isNonLocal() && !revision.getFile().isDirectory(); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RepositoryChangesBrowser.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RepositoryChangesBrowser.java index a595c8ffb516..9c66bba3d2d9 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RepositoryChangesBrowser.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RepositoryChangesBrowser.java @@ -71,7 +71,9 @@ public class RepositoryChangesBrowser extends ChangesBrowser implements DataProv protected void buildToolBar(final DefaultActionGroup toolBarGroup) { super.buildToolBar(toolBarGroup); - toolBarGroup.add(new ShowDiffWithLocalAction()); + toolBarGroup.add(new ShowDiffWithLocalAction(true)); + toolBarGroup.add(new ShowDiffWithLocalAction(false)); + myEditSourceAction = new MyEditSourceAction(); myEditSourceAction.registerCustomShortcutSet(CommonShortcuts.getEditSource(), this); toolBarGroup.add(myEditSourceAction); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java index 881bc0c35a44..a437986e8c60 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java @@ -408,7 +408,9 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton impleme diffAction.registerCustomShortcutSet(CommonShortcuts.getDiff(), null); result.add(diffAction); - result.add(ActionManager.getInstance().getAction("Vcs.ShowDiffWithLocal")); + if (popup) { + result.add(ActionManager.getInstance().getAction("Vcs.ShowDiffWithLocal")); + } final AnAction diffGroup = ActionManager.getInstance().getAction(VCS_HISTORY_ACTIONS_GROUP); if (diffGroup != null) result.add(diffGroup); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffWithLocalAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffAfterWithLocalAction.java similarity index 94% rename from platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffWithLocalAction.java rename to platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffAfterWithLocalAction.java index 29e7c31fb2af..d1307d4ec7cc 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffWithLocalAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffAfterWithLocalAction.java @@ -30,11 +30,11 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; -public class ShowDiffWithLocalAction extends ExtendableAction implements DumbAware { +public class ShowDiffAfterWithLocalAction extends ExtendableAction implements DumbAware { private static final ExtensionPointName EP_NAME = - ExtensionPointName.create("com.intellij.openapi.vcs.history.actions.ShowDiffWithLocalAction.ExtensionProvider"); + ExtensionPointName.create("com.intellij.openapi.vcs.history.actions.ShowDiffAfterWithLocalAction.ExtensionProvider"); - public ShowDiffWithLocalAction() { + public ShowDiffAfterWithLocalAction() { super(EP_NAME); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffBeforeWithLocalAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffBeforeWithLocalAction.java new file mode 100644 index 000000000000..08f4dde1b9a6 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffBeforeWithLocalAction.java @@ -0,0 +1,41 @@ +/* + * 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.vcs.history.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.AnActionExtensionProvider; +import com.intellij.openapi.actionSystem.ExtendableAction; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.project.DumbAware; +import org.jetbrains.annotations.NotNull; + +public class ShowDiffBeforeWithLocalAction extends ExtendableAction implements DumbAware { + private static final ExtensionPointName EP_NAME = + ExtensionPointName.create("com.intellij.openapi.vcs.history.actions.ShowDiffBeforeWithLocalAction.ExtensionProvider"); + + public ShowDiffBeforeWithLocalAction() { + super(EP_NAME); + } + + @Override + public void defaultActionPerformed(@NotNull AnActionEvent e) { + } + + @Override + public void defaultUpdate(@NotNull AnActionEvent e) { + e.getPresentation().setEnabledAndVisible(false); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java index 00b63eec5489..f1cc469fcb26 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java @@ -208,7 +208,7 @@ public class VcsSelectionHistoryDialog extends FrameWrapper implements DataProvi final DefaultActionGroup popupActions = new DefaultActionGroup(); popupActions.add(new MyDiffAction()); - popupActions.add(new MyDiffLocalAction()); + popupActions.add(new MyDiffAfterWithLocalAction()); popupActions.add(ShowAllAffectedGenericAction.getInstance()); popupActions.add(ActionManager.getInstance().getAction(VcsActions.ACTION_COPY_REVISION_NUMBER)); PopupHandler.installPopupHandler(myList, popupActions, ActionPlaces.UPDATE_POPUP, ActionManager.getInstance()); @@ -496,8 +496,8 @@ public class VcsSelectionHistoryDialog extends FrameWrapper implements DataProvi } } - private class MyDiffLocalAction extends DumbAwareAction { - public MyDiffLocalAction() { + private class MyDiffAfterWithLocalAction extends DumbAwareAction { + public MyDiffAfterWithLocalAction() { ActionUtil.copyFrom(this, "Vcs.ShowDiffWithLocal"); } diff --git a/platform/vcs-log/impl/src/META-INF/vcs-log.xml b/platform/vcs-log/impl/src/META-INF/vcs-log.xml index 3a6fcc926c5f..7d8a9ff404cc 100644 --- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml +++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml @@ -37,7 +37,8 @@ - + + @@ -122,6 +123,7 @@ + @@ -132,7 +134,6 @@ - @@ -176,6 +177,7 @@ + diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java index dade82b4dffa..a30cddb645bf 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java @@ -129,7 +129,7 @@ public class FileHistoryUi extends AbstractVcsLogUi { } @Nullable - public FilePath getPath(@NotNull VcsFullCommitDetails details) { + public FilePath getAfterPath(@NotNull VcsFullCommitDetails details) { if (myPath.isDirectory()) return myPath; List changes = collectRelevantChanges(details); @@ -143,6 +143,21 @@ public class FileHistoryUi extends AbstractVcsLogUi { return null;// file was deleted } + @Nullable + public FilePath getBeforePath(@NotNull VcsFullCommitDetails details) { + if (myPath.isDirectory()) return myPath; + + List changes = collectRelevantChanges(details); + for (Change change : changes) { + ContentRevision revision = change.getBeforeRevision(); + if (revision != null) { + return revision.getFile(); + } + } + + return null;// file was created + } + @NotNull public List collectRelevantChanges(@NotNull VcsFullCommitDetails details) { Set fileNames = getFileNames(details); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentProvider.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentProvider.java index e6c8a27ca593..e054b4e89a81 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentProvider.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentProvider.java @@ -16,6 +16,7 @@ package com.intellij.vcs.log.impl; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vcs.ProjectLevelVcsManager; @@ -49,6 +50,7 @@ import java.util.List; * Delegates to the VcsLogManager. */ public class VcsLogContentProvider implements ChangesViewContentProvider { + private static final Logger LOG = Logger.getInstance(VcsLogContentProvider.class); public static final String TAB_NAME = "Log"; @NotNull private final Project myProject; @@ -167,6 +169,7 @@ public class VcsLogContentProvider implements ChangesViewContentProvider { for (String tabName : logManager.getTabNames()) { if (!TAB_NAME.equals(tabName)) { // main tab is closed by the ChangesViewContentManager Content content = toolWindow.getContentManager().findContent(tabName); + LOG.assertTrue(content != null, "Could not find content for tab " + tabName); ContentsUtil.closeContentTab(toolWindow.getContentManager(), content); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/CompareRevisionsFromHistoryActionProvider.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/CompareRevisionsFromHistoryActionProvider.java index ea2dfc46f6e2..d698ed3ca2fa 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/CompareRevisionsFromHistoryActionProvider.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/CompareRevisionsFromHistoryActionProvider.java @@ -113,8 +113,8 @@ public class CompareRevisionsFromHistoryActionProvider implements AnActionExtens // so that it could return a single file path for each revision VcsFullCommitDetails newestDetail = details.get(0); VcsFullCommitDetails olderDetail = details.get(1); - notNull(handler).showDiff(olderDetail.getRoot(), ui.getPath(olderDetail), olderDetail.getId(), - ui.getPath(newestDetail), newestDetail.getId()); + notNull(handler).showDiff(olderDetail.getRoot(), ui.getAfterPath(olderDetail), olderDetail.getId(), + ui.getAfterPath(newestDetail), newestDetail.getId()); } else if (details.size() == 1) { VcsFullCommitDetails detail = notNull(ContainerUtil.getFirstItem(details)); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffWithLocalFromHistoryActionProvider.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffAfterWithLocalFromHistoryActionProvider.java similarity index 84% rename from platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffWithLocalFromHistoryActionProvider.java rename to platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffAfterWithLocalFromHistoryActionProvider.java index 6b0501b5b256..dcd3e2f63b70 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffWithLocalFromHistoryActionProvider.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffAfterWithLocalFromHistoryActionProvider.java @@ -28,19 +28,12 @@ import com.intellij.vcs.log.ui.VcsLogInternalDataKeys; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class ShowDiffWithLocalFromHistoryActionProvider extends FileHistorySingleCommitAction implements AnActionExtensionProvider { +public class ShowDiffAfterWithLocalFromHistoryActionProvider extends FileHistorySingleCommitAction implements AnActionExtensionProvider { @Override public boolean isActive(@NotNull AnActionEvent e) { return e.getData(VcsLogInternalDataKeys.FILE_HISTORY_UI) != null; } - @Override - public void update(@NotNull AnActionEvent e) { - super.update(e); - - e.getPresentation().setDescription("Compare selected revision with the local version of the file"); - } - @Override protected boolean isEnabled(@NotNull FileHistoryUi ui, @Nullable VcsFullCommitDetails detail, @NotNull AnActionEvent e) { FilePath filePath = e.getData(VcsDataKeys.FILE_PATH); @@ -59,6 +52,6 @@ public class ShowDiffWithLocalFromHistoryActionProvider extends FileHistorySingl FilePath path = e.getRequiredData(VcsDataKeys.FILE_PATH); VcsLogDiffHandler handler = e.getRequiredData(VcsLogInternalDataKeys.LOG_DIFF_HANDLER); - handler.showDiffWithLocal(detail.getRoot(), ui.getPath(detail), detail.getId(), path); + handler.showDiffWithLocal(detail.getRoot(), ui.getAfterPath(detail), detail.getId(), path); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffBeforeWithLocalFromHistoryActionProvider.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffBeforeWithLocalFromHistoryActionProvider.java new file mode 100644 index 000000000000..6e061bea210b --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffBeforeWithLocalFromHistoryActionProvider.java @@ -0,0 +1,66 @@ +/* + * 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.vcs.log.ui.actions.history; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.AnActionExtensionProvider; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsDataKeys; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsLogDiffHandler; +import com.intellij.vcs.log.history.FileHistoryUi; +import com.intellij.vcs.log.ui.VcsLogInternalDataKeys; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ShowDiffBeforeWithLocalFromHistoryActionProvider extends FileHistorySingleCommitAction implements AnActionExtensionProvider { + @Override + public boolean isActive(@NotNull AnActionEvent e) { + return e.getData(VcsLogInternalDataKeys.FILE_HISTORY_UI) != null; + } + + @Override + protected boolean isEnabled(@NotNull FileHistoryUi ui, @Nullable VcsFullCommitDetails detail, @NotNull AnActionEvent e) { + FilePath filePath = e.getData(VcsDataKeys.FILE_PATH); + VcsLogDiffHandler handler = e.getData(VcsLogInternalDataKeys.LOG_DIFF_HANDLER); + + if (filePath == null || filePath.getVirtualFile() == null || handler == null) { + return false; + } + + if (detail != null) { + return detail.getParents().size() == 1; + } + + return true; + } + + @Override + protected void performAction(@NotNull Project project, + @NotNull FileHistoryUi ui, + @NotNull VcsFullCommitDetails detail, + @NotNull AnActionEvent e) { + if (ChangeListManager.getInstance(project).isFreezedWithNotification(null)) return; + if (detail.getParents().size() != 1) return; + + FilePath path = e.getRequiredData(VcsDataKeys.FILE_PATH); + VcsLogDiffHandler handler = e.getRequiredData(VcsLogInternalDataKeys.LOG_DIFF_HANDLER); + + handler.showDiffWithLocal(detail.getRoot(), ui.getBeforePath(detail), detail.getParents().get(0), path); + } +} diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties index 0739e89860ad..04cdf4171ade 100644 --- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties +++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties @@ -460,13 +460,13 @@ vfs.listener.delete.title=Delete Files from Git general.error = Git error executable.error.title = Git executable problem -executable.error.description = Git couldn't be started. Probably the path to Git executable is not valid. Fix it. +executable.error.description = Git couldn't be started. The path to the Git executable is probably not valid. Fix it. git.commit.message.empty=Please specify commit message git.commit.message.empty.title=Commit Message Is Empty git.executable.notification.title=Can't start Git -git.executable.notification.description=Probably the path to Git executable is not valid. +git.executable.notification.description=The path to Git executable is probably not valid. git.executable.dialog.title=Git executable git.executable.dialog.description=Specify the full path to Git executable git.executable.dialog.error=It doesn't appear to be a valid Git executable @@ -485,4 +485,4 @@ vcs.popup.git.github.section=GitHub vcs.history.action.gitlog=Select in Git Log vcs.history.action.gitlog.error=Git Log is not Ready -exportable.Git.Application.Settings.presentable.name=Git \ No newline at end of file +exportable.Git.Application.Settings.presentable.name=Git diff --git a/plugins/git4idea/src/git4idea/push/GitPushOptionsPanel.java b/plugins/git4idea/src/git4idea/push/GitPushOptionsPanel.java index fc1defc19b84..ef302d32b1a2 100644 --- a/plugins/git4idea/src/git4idea/push/GitPushOptionsPanel.java +++ b/plugins/git4idea/src/git4idea/push/GitPushOptionsPanel.java @@ -70,16 +70,20 @@ public class GitPushOptionsPanel extends VcsPushOptionsPanel { myRunHooks.setSelected(true); myRunHooks.setVisible(showSkipHookOption); - myRunHooks.setBorder(JBUI.Borders.empty()); - myPushTagsMode.setBorder(JBUI.Borders.empty()); - myPushTags.setBorder(JBUI.Borders.empty()); - setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); add(myPushTags); - add(Box.createHorizontalStrut(JBUI.scale(3))); - add(myPushTagsMode); - add(Box.createHorizontalStrut(JBUI.scale(37))); - add(myRunHooks); + if (myPushTagsMode.isVisible()) { + add(Box.createHorizontalStrut(calcStrutWidth(8, myPushTags, myPushTagsMode))); + add(myPushTagsMode); + } + if (myRunHooks.isVisible()) { + add(Box.createHorizontalStrut(calcStrutWidth(40, myPushTagsMode, myRunHooks))); + add(myRunHooks); + } + } + + private static int calcStrutWidth(int plannedWidth, @NotNull JComponent leftComponent, @NotNull JComponent rightComponent) { + return JBUI.scale(plannedWidth) - JBUI.insets(rightComponent.getInsets()).left - JBUI.insets(leftComponent.getInsets()).right; } @Nullable diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index ae269d3a234a..592baa420813 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -335,6 +335,8 @@ + diff --git a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties index 16b2f9a7d4d3..45492b96de6f 100644 --- a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties +++ b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties @@ -65,7 +65,7 @@ hg4idea.commit.partial.merge.title=Partial Merge Commit hg4idea.commit.error.messageEmpty=Please provide a commit message hg4idea.executable.notification.title=Can't start Mercurial -hg4idea.executable.notification.description=Probably the path to hg executable is not valid. +hg4idea.executable.notification.description=The path to the hg executable is probably not valid. hg4idea.init.dialog.title=Create Mercurial repository hg4idea.init.dialog.incorrect.path=The specified path is incorrect diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java index 3b7239fd5702..196753d84891 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java @@ -40,12 +40,12 @@ import java.util.Set; public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { public static final DefaultLightProjectDescriptor JAVA_FX_WITH_GROOVY_DESCRIPTOR = new DefaultLightProjectDescriptor() { @Override - public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { + public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { AbstractJavaFXTestCase.addJavaFxJarAsLibrary(module, model); - PsiTestUtil.addLibrary(module, model, "javafx", PluginPathManager.getPluginHomePath("javaFX") + "/testData", "groovy-1.8.0.jar"); - super.configureModule(module, model, contentEntry); - } - }; + PsiTestUtil.addLibrary(module, model, "javafx", PluginPathManager.getPluginHomePath("javaFX") + "/testData", "groovy-1.8.0.jar"); + super.configureModule(module, model, contentEntry); + } + }; @NotNull @Override @@ -106,7 +106,8 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { } public void testCreateMethodEscalateVisibility() throws Exception { - doTestWithDefaultVisibility("Create method 'void onAction(ActionEvent)'", "CreateMethod", VisibilityUtil.ESCALATE_VISIBILITY, ".java"); + doTestWithDefaultVisibility("Create method 'void onAction(ActionEvent)'", "CreateMethod", VisibilityUtil.ESCALATE_VISIBILITY, + ".java"); } public void testCreateFieldEmptyName() throws Exception { @@ -122,7 +123,8 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { assertNotNull(intention); Set languages = JavaFxInjectPageLanguageIntention.getAvailableLanguages(getProject()); assertContainsElements(languages, "groovy"); - JavaFxInjectPageLanguageIntention languageIntention = (JavaFxInjectPageLanguageIntention)((IntentionActionDelegate)intention).getDelegate(); + JavaFxInjectPageLanguageIntention languageIntention = + (JavaFxInjectPageLanguageIntention)((IntentionActionDelegate)intention).getDelegate(); languageIntention.registerPageLanguage(getProject(), (XmlFile)myFixture.getFile(), "groovy"); myFixture.checkResultByFile(getTestName(true) + ".fxml", getTestName(true) + "_after.fxml", true); } @@ -168,7 +170,7 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { super.setUp(); myFixture.enableInspections(new JavaFxUnresolvedFxIdReferenceInspection()); } - + @NotNull @Override protected String getTestDataPath() { diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxEventHandlerReference.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxEventHandlerReference.java index e359dd4bfc7b..b4fce902a350 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxEventHandlerReference.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxEventHandlerReference.java @@ -16,12 +16,16 @@ package org.jetbrains.plugins.javaFX.fxml.refs; import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; -import com.intellij.codeInsight.daemon.impl.quickfix.CreateMethodQuickFix; +import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory; +import com.intellij.codeInsight.intention.MethodInsertionInfo; import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.SuggestedNameInfo; +import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.psi.xml.XmlAttribute; @@ -34,6 +38,7 @@ import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames; import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class JavaFxEventHandlerReference extends PsiReferenceBase { @@ -59,8 +64,8 @@ public class JavaFxEventHandlerReference extends PsiReferenceBase availableHandlers = new ArrayList<>(); for (PsiMethod psiMethod : myController.getAllMethods()) { if (isHandlerMethodSignature(psiMethod, myController) && JavaFxPsiUtil.isVisibleInFxml(psiMethod)) { - availableHandlers.add(psiMethod); - } + availableHandlers.add(psiMethod); + } } return availableHandlers.isEmpty() ? EMPTY_ARRAY : ArrayUtil.toObjectArray(availableHandlers); } @@ -92,30 +97,58 @@ public class JavaFxEventHandlerReference extends PsiReferenceBase { + public static class JavaFxUnresolvedReferenceHandlerQuickfixProvider + extends UnresolvedReferenceQuickFixProvider { @Override public void registerFixes(@NotNull final JavaFxEventHandlerReference ref, @NotNull final QuickFixActionRegistrar registrar) { if (ref.myController != null && ref.myEventHandler == null) { - final CreateMethodQuickFix quickFix = CreateMethodQuickFix.createFix(ref.myController, getHandlerSignature(ref), ""); - if (quickFix != null) { - registrar.register(quickFix); - } + JvmCommonIntentionActionsFactory intentionActionsFactory = + JvmCommonIntentionActionsFactory.forLanguage(ref.myController.getLanguage()); + if (intentionActionsFactory == null) return; + + String javaSignature = getHandlerSignature(ref); + PsiMethod javaMethod = JavaPsiFacade.getElementFactory(ref.myController.getProject()) + .createMethodFromText(javaSignature, ref.myController); + + MethodInsertionInfo.Method method = + MethodInsertionInfo.simpleMethodInfo(ref.myController, + javaMethod.getName(), + javaMethod.getModifierList().getText(), + javaMethod.getReturnType(), + Arrays.asList(javaMethod.getParameterList().getParameters())); + intentionActionsFactory.createAddCallableMemberActions(method).forEach(registrar::register); } } private static String getHandlerSignature(JavaFxEventHandlerReference ref) { final XmlAttributeValue element = ref.getElement(); - String canonicalText = JavaFxCommonNames.JAVAFX_EVENT; + PsiType eventType = getEventType(element); + final String modifiers = getModifiers(element.getProject()); + return modifiers + " void " + element.getValue().substring(1) + "(" + + eventType.getCanonicalText() + " " + suggestParamName(element.getProject(), eventType) + + ")"; + } + + private static String suggestParamName(Project project, PsiType eventType) { + SuggestedNameInfo suggestedInfo = JavaCodeStyleManager.getInstance(project) + .suggestVariableName(VariableKind.PARAMETER, null, null, eventType); + if (suggestedInfo.names.length < 1) { + return "e"; + } + return suggestedInfo.names[0]; + } + + @NotNull + private static PsiType getEventType(XmlAttributeValue element) { final PsiElement parent = element.getParent(); if (parent instanceof XmlAttribute) { final PsiClassType eventType = JavaFxPsiUtil.getDeclaredEventType((XmlAttribute)parent); if (eventType != null) { - canonicalText = eventType.getCanonicalText(); + return eventType; } } - final String modifiers = getModifiers(element.getProject()); - return modifiers + " void " + element.getValue().substring(1) + "(" + canonicalText + " e)"; + return PsiType.getTypeByName(JavaFxCommonNames.JAVAFX_EVENT, element.getProject(), element.getResolveScope()); } @NotNull diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixHalfRaw_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixHalfRaw_after.java index 95e7e418114f..3ee137da3bd9 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixHalfRaw_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixHalfRaw_after.java @@ -7,6 +7,5 @@ public class QuickfixHalfRaw { @FXML TableView table; public void onSort(SortEvent> tableViewSortEvent) { - } } diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoFieldNested_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoFieldNested_after.java index dbd3ba34beae..3c31e95de47f 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoFieldNested_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoFieldNested_after.java @@ -2,6 +2,5 @@ import javafx.scene.control.TableColumn.CellEditEvent; public class QuickfixNoFieldNested { public void onColumnEditStart(CellEditEvent cellEditEvent) { - } } diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoField_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoField_after.java index 55046662bd9f..909821015de3 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoField_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoField_after.java @@ -3,6 +3,5 @@ import javafx.scene.control.TableView; public class QuickfixNoField { public void onSort(SortEvent tableViewSortEvent) { - } } diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixRaw_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixRaw_after.java index 73bd806e4d9c..d837356bd973 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixRaw_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixRaw_after.java @@ -6,6 +6,5 @@ public class QuickfixRaw { @FXML TableView table; public void onSort(SortEvent tableViewSortEvent) { - } } diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixSpecific_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixSpecific_after.java index 9ad1f4c4332f..e94b44ce825d 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixSpecific_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixSpecific_after.java @@ -8,6 +8,5 @@ public class QuickfixSpecific { TableView> table; public void onSort(SortEvent>> tableViewSortEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixSuper_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixSuper_after.java index 02ba66adbea2..37f1e025890b 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixSuper_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixSuper_after.java @@ -2,6 +2,5 @@ import javafx.scene.input.MouseEvent; public class QuickfixSuper{ public void click(MouseEvent mouseEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateControllerMethodGeneric_after.java b/plugins/javaFX/testData/quickfix/CreateControllerMethodGeneric_after.java index 25662ad5bfe1..49a0540150cd 100644 --- a/plugins/javaFX/testData/quickfix/CreateControllerMethodGeneric_after.java +++ b/plugins/javaFX/testData/quickfix/CreateControllerMethodGeneric_after.java @@ -9,6 +9,5 @@ public class CreateControllerMethodGeneric { TableView> table; public void onSort(SortEvent>> tableViewSortEvent) { - } } diff --git a/plugins/javaFX/testData/quickfix/CreateControllerMethodHalfRaw_after.java b/plugins/javaFX/testData/quickfix/CreateControllerMethodHalfRaw_after.java index d064fe1f4bb2..6942764dd744 100644 --- a/plugins/javaFX/testData/quickfix/CreateControllerMethodHalfRaw_after.java +++ b/plugins/javaFX/testData/quickfix/CreateControllerMethodHalfRaw_after.java @@ -9,6 +9,5 @@ public class CreateControllerMethodHalfRaw { TableView table; public void onSort(SortEvent> tableViewSortEvent) { - } } diff --git a/plugins/javaFX/testData/quickfix/CreateControllerMethodInGroovy_after.groovy b/plugins/javaFX/testData/quickfix/CreateControllerMethodInGroovy_after.groovy index 9537d414dc21..bddfc00fe53b 100644 --- a/plugins/javaFX/testData/quickfix/CreateControllerMethodInGroovy_after.groovy +++ b/plugins/javaFX/testData/quickfix/CreateControllerMethodInGroovy_after.groovy @@ -1,7 +1,5 @@ import javafx.event.ActionEvent public class CreateControllerMethodInGroovy { - public void bar(ActionEvent actionEvent) { - - } + public void bar(ActionEvent actionEvent) {} } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateControllerMethod_after.java b/plugins/javaFX/testData/quickfix/CreateControllerMethod_after.java index d410e79b41fe..193f7642c3ae 100644 --- a/plugins/javaFX/testData/quickfix/CreateControllerMethod_after.java +++ b/plugins/javaFX/testData/quickfix/CreateControllerMethod_after.java @@ -2,6 +2,5 @@ import javafx.event.ActionEvent; public class CreateControllerMethod { public void bar(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateMethodEscalateVisibility_after.java b/plugins/javaFX/testData/quickfix/CreateMethodEscalateVisibility_after.java index 20913689cd15..9b9a6dfdf051 100644 --- a/plugins/javaFX/testData/quickfix/CreateMethodEscalateVisibility_after.java +++ b/plugins/javaFX/testData/quickfix/CreateMethodEscalateVisibility_after.java @@ -4,6 +4,5 @@ import javafx.fxml.FXML; public class CreateMethod { @FXML private void onAction(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateMethodPackageLocalVisibility_after.java b/plugins/javaFX/testData/quickfix/CreateMethodPackageLocalVisibility_after.java index db22d3ef4123..94777309d719 100644 --- a/plugins/javaFX/testData/quickfix/CreateMethodPackageLocalVisibility_after.java +++ b/plugins/javaFX/testData/quickfix/CreateMethodPackageLocalVisibility_after.java @@ -4,6 +4,5 @@ import javafx.fxml.FXML; public class CreateMethod { @FXML void onAction(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateMethodPrivateVisibility_after.java b/plugins/javaFX/testData/quickfix/CreateMethodPrivateVisibility_after.java index 20913689cd15..9b9a6dfdf051 100644 --- a/plugins/javaFX/testData/quickfix/CreateMethodPrivateVisibility_after.java +++ b/plugins/javaFX/testData/quickfix/CreateMethodPrivateVisibility_after.java @@ -4,6 +4,5 @@ import javafx.fxml.FXML; public class CreateMethod { @FXML private void onAction(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateMethodProtectedVisibility_after.java b/plugins/javaFX/testData/quickfix/CreateMethodProtectedVisibility_after.java index 8ab76bb9ccb8..a09c8da422cf 100644 --- a/plugins/javaFX/testData/quickfix/CreateMethodProtectedVisibility_after.java +++ b/plugins/javaFX/testData/quickfix/CreateMethodProtectedVisibility_after.java @@ -4,6 +4,5 @@ import javafx.fxml.FXML; public class CreateMethod { @FXML protected void onAction(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateMethodPublicVisibility_after.java b/plugins/javaFX/testData/quickfix/CreateMethodPublicVisibility_after.java index 36687f750c0d..8233bee8152a 100644 --- a/plugins/javaFX/testData/quickfix/CreateMethodPublicVisibility_after.java +++ b/plugins/javaFX/testData/quickfix/CreateMethodPublicVisibility_after.java @@ -2,6 +2,5 @@ import javafx.event.ActionEvent; public class CreateMethod { public void onAction(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java b/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java index a6e54d5238da..4e1e085c7c85 100644 --- a/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java +++ b/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java @@ -23,7 +23,9 @@ import com.intellij.psi.filters.ElementFilter; import com.intellij.psi.filters.position.FilterPattern; import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.ProcessingContext; +import com.siyeh.ig.junit.JUnitCommonClassNames; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -33,13 +35,13 @@ public class JUnitReferenceContributor extends PsiReferenceContributor { } public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { - registrar.registerReferenceProvider(getElementPattern("org.junit.jupiter.params.provider.MethodSource", "value"), new PsiReferenceProvider() { + registrar.registerReferenceProvider(getElementPattern(JUnitCommonClassNames.ORG_JUNIT_JUPITER_PARAMS_PROVIDER_METHOD_SOURCE, "value"), new PsiReferenceProvider() { @NotNull public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) { return new MethodSourceReference[]{new MethodSourceReference((PsiLiteral)element)}; } }); - registrar.registerReferenceProvider(getElementPattern("org.junit.jupiter.params.provider.CsvFileSource", "resources"), new PsiReferenceProvider() { + registrar.registerReferenceProvider(getElementPattern(JUnitCommonClassNames.ORG_JUNIT_JUPITER_PARAMS_PROVIDER_CSV_FILE_SOURCE, "resources"), new PsiReferenceProvider() { @NotNull public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) { return FileReferenceSet.createSet(element, false, false, false).getAllReferences(); @@ -60,7 +62,8 @@ public class JUnitReferenceContributor extends PsiReferenceContributor { public boolean isAcceptable(Object element, PsiElement context) { PsiNameValuePair pair = PsiTreeUtil.getParentOfType(context, PsiNameValuePair.class, false, PsiMember.class, PsiStatement.class); if (pair == null) return false; - if (!myParameterName.equals(pair.getName())) return false; + String name = ObjectUtils.notNull(pair.getName(), PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME); + if (!myParameterName.equals(name)) return false; PsiAnnotation annotation = PsiTreeUtil.getParentOfType(pair, PsiAnnotation.class); if (annotation == null) return false; return myAnnotation.equals(annotation.getQualifiedName()); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties index 16f2a17ca55f..0df6f9253a5e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties @@ -1,7 +1,7 @@ subversion.name=Subversion subversion.executable.notification.title=Can't use Subversion command line client -subversion.executable.notification.description=Probably the path to Subversion executable is wrong. +subversion.executable.notification.description=The path to the Subversion executable is probably wrong. subversion.executable.too.old=Subversion command line client version is too old ({0}). subversion.roots.detection.errors.found.description=Errors found while detecting svn working copies. Fix it. @@ -683,4 +683,4 @@ dialog.create.branch.or.tag.from.working.copy.warning=Use this variant to

Each file that have revision different than root will be copied separately.\
It's recommended to update working copy before branch creation therefore. -exportable.SvnDiffSettings.presentable.name=Svn Diff \ No newline at end of file +exportable.SvnDiffSettings.presentable.name=Svn Diff diff --git a/python/educational-core/resources/META-INF/plugin.xml b/python/educational-core/resources/META-INF/plugin.xml index 78f53d33f08b..6620364701e7 100644 --- a/python/educational-core/resources/META-INF/plugin.xml +++ b/python/educational-core/resources/META-INF/plugin.xml @@ -84,7 +84,9 @@ text="Switch Task Description Panel"> - + + + diff --git a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java index d6429b5bfce3..ce88e1ce27d0 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java @@ -119,8 +119,7 @@ public interface EduPluginConfigurator { StudyPreviousTaskAction.ACTION_ID, StudyNextTaskAction.ACTION_ID, StudyRefreshTaskFileAction.ACTION_ID, - StudyShowHintAction.ACTION_ID, - StudyUpdateRecommendationAction.ACTION_ID + StudyShowHintAction.ACTION_ID }; ActionManager actionManager = ActionManager.getInstance(); Arrays.stream(ids) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyUpdateRecommendationAction.java b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyUpdateRecommendationAction.java index dde281bf07c0..cd9499ffe577 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyUpdateRecommendationAction.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyUpdateRecommendationAction.java @@ -19,10 +19,9 @@ import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector; import icons.EducationalCoreIcons; public class StudyUpdateRecommendationAction extends DumbAwareAction { - public static final String ACTION_ID = "Edu.UpdateRecommendation"; public StudyUpdateRecommendationAction() { - super("Update course", "Update course", EducationalCoreIcons.StepikRefresh); + super("Synchronize course", "Synchronize course", EducationalCoreIcons.StepikRefresh); } @Override @@ -43,7 +42,7 @@ public class StudyUpdateRecommendationAction extends DumbAwareAction { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); return StudyUtils.execCancelable(() -> EduAdaptiveStepicConnector.getNextRecommendation(project, (RemoteCourse)course)); }, - "Updating Course", true, project); + "Synchronizing Course", true, project); if (lastRecommendationOnStepik != null && lastRecommendationOnStepik.getStepId() != lastRecommendationInCourse.getStepId()) { lastRecommendationOnStepik.initTask(adaptiveLesson, false); diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java b/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java index 2727464338b4..203ed3341d61 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyFunction.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. @@ -53,9 +53,6 @@ public interface PyFunction extends PsiNamedElement, StubBasedPsiElement getReturnType(@NotNull PyCallable callable, @NotNull TypeEvalContext context) { if (callable instanceof PyFunction) { final PyFunction function = (PyFunction)callable; - // We model generic classes as return types of their constructors here - final boolean isInit = PyUtil.isInit(function); - if (isInit) { - final PyClass cls = function.getContainingClass(); - if (cls != null) { - final PyType genericType = getGenericType(cls, context); - if (genericType != null) { - return Ref.create(genericType); - } - } - } final PyExpression value = getReturnTypeAnnotation(function); if (value != null) { final Ref typeRef = getType(value, new Context(context)); if (typeRef != null) { - if (isInit && typeRef.get() instanceof PyNoneType) { - return null; - } if (function.isAsync() && function.isAsyncAllowed() && !function.isGenerator()) { return Ref.create(wrapInCoroutineType(typeRef.get(), callable)); } @@ -424,6 +410,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final PyTupleExpression tupleExpr = as(e, PyTupleExpression.class); return tupleExpr != null ? StreamEx.of(tupleExpr.getElements()) : StreamEx.of(e); }) + .nonNull() .flatMap(e -> tryResolving(e, typeEvalContext).stream()) .map(e -> getGenericTypeFromTypeVar(e, context)) .select(PyType.class) @@ -495,10 +482,6 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (genericType != null) { return Ref.create(genericType); } - final Ref classType = getClassType(resolved, context.getTypeContext()); - if (classType != null) { - return classType; - } final PyType stringBasedType = getStringBasedType(resolved, context); if (stringBasedType != null) { return Ref.create(stringBasedType); @@ -507,6 +490,10 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (anyType != null) { return anyType; } + final Ref classType = getClassType(resolved, context.getTypeContext()); + if (classType != null) { + return classType; + } return null; } finally { diff --git a/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java b/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java index a43bfa5936ae..737ee7b8e93c 100644 --- a/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java +++ b/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java @@ -58,16 +58,11 @@ public class PyDocStringTypeProvider extends PyTypeProviderBase { @Nullable @Override public Ref getReturnType(@NotNull PyCallable callable, @NotNull TypeEvalContext context) { - if (callable instanceof PyDocStringOwner) { - final StructuredDocString docString = ((PyDocStringOwner)callable).getStructuredDocString(); - if (docString != null) { - final String typeText = docString.getReturnType(); - if (StringUtil.isNotEmpty(typeText)) { - return parseType(callable, typeText, context); - } - } + if (callable instanceof PyFunction && PyUtil.isInit((PyFunction)callable)) { + return null; } - return null; + + return getReturnTypeFromDocString(callable, context); } @NotNull @@ -87,12 +82,23 @@ public class PyDocStringTypeProvider extends PyTypeProviderBase { public PyType getGenericType(@NotNull PyClass cls, @NotNull TypeEvalContext context) { final PyFunction init = cls.findInitOrNew(true, context); if (init != null) { - final PyType initType = context.getType(init); - final PyCallableType callableType = PyUtil.as(initType, PyCallableType.class); - if (callableType != null) { - return callableType.getReturnType(context); - } + return Ref.deref(getReturnTypeFromDocString(init, context)); } return null; } + + @Nullable + private static Ref getReturnTypeFromDocString(@NotNull PyCallable callable, @NotNull TypeEvalContext context) { + if (callable instanceof PyDocStringOwner) { + final StructuredDocString docString = ((PyDocStringOwner)callable).getStructuredDocString(); + if (docString != null) { + final String typeText = docString.getReturnType(); + if (StringUtil.isNotEmpty(typeText)) { + return parseType(callable, typeText, context); + } + } + } + + return null; + } } diff --git a/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java index f3f81ad6473e..24a3c1f4cf51 100644 --- a/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.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. @@ -78,10 +78,7 @@ public class PyMethodMayBeStaticInspection extends PyInspection { if (!attributes.isEmpty()) return; if (isTestElement(node)) return; - final PyStatementList statementList = node.getStatementList(); - final PyStatement[] statements = statementList.getStatements(); - - if (statements.length == 1 && statements[0] instanceof PyPassStatement) return; + if (PyUtil.isEmptyFunction(node)) return; final PyParameter[] parameters = node.getParameterList().getParameters(); diff --git a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java index c128b29ff73b..ee3ce51d5030 100644 --- a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java @@ -139,9 +139,8 @@ public class PyTypeCheckerInspection extends PyInspection { final String typeCommentAnnotation = node.getTypeCommentAnnotation(); if (annotation != null || typeCommentAnnotation != null) { if (!PyUtil.isEmptyFunction(node)) { - final PyStatementList statements = node.getStatementList(); - ReturnVisitor visitor = new ReturnVisitor(node); - statements.accept(visitor); + final ReturnVisitor visitor = new ReturnVisitor(node); + node.getStatementList().accept(visitor); if (!visitor.myHasReturns) { final PyType expected = getExpectedReturnType(node); final String expectedName = PythonDocumentationProvider.getTypeName(expected, myTypeEvalContext); @@ -151,6 +150,11 @@ public class PyTypeCheckerInspection extends PyInspection { } } } + + if (PyUtil.isInit(node) && !(getExpectedReturnType(node) instanceof PyNoneType)) { + registerProblem(annotation != null ? annotation.getValue() : node.getTypeComment(), + PyNames.INIT + " should return " + PyNames.NONE); + } } } diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 7b048a27d616..76ec28d37e26 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -72,10 +72,7 @@ import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.formatter.PyCodeStyleSettings; import com.jetbrains.python.magicLiteral.PyMagicLiteralTools; -import com.jetbrains.python.psi.impl.PyBuiltinCache; -import com.jetbrains.python.psi.impl.PyPsiUtils; -import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl; -import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher; +import com.jetbrains.python.psi.impl.*; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.QualifiedNameFinder; import com.jetbrains.python.psi.resolve.RatedResolveResult; @@ -941,6 +938,28 @@ public class PyUtil { } } + @Nullable + public static PyType getReturnTypeToAnalyzeAsCallType(@NotNull PyFunction function, @NotNull TypeEvalContext context) { + if (isInit(function)) { + final PyClass cls = function.getContainingClass(); + if (cls != null) { + for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { + final PyType providedClassType = provider.getGenericType(cls, context); + if (providedClassType != null) { + return providedClassType; + } + } + + final PyInstantiableType classType = as(context.getType(cls), PyInstantiableType.class); + if (classType != null) { + return classType.toInstance(); + } + } + } + + return context.getReturnType(function); + } + public static class KnownDecoratorProviderHolder { public static final PyKnownDecoratorProvider[] KNOWN_DECORATOR_PROVIDERS = Extensions.getExtensions(PyKnownDecoratorProvider.EP_NAME); diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 012a89cecfec..4703a00ec7d1 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -567,10 +567,10 @@ public class PyCallExpressionHelper { return Ref.create(new PyClassTypeImpl(cls, false)); } - if (t != null && !(t instanceof PyNoneType)) { + if (t != null) { return Ref.create(t); } - if (cls != null && t == null) { + if (cls != null) { final PyFunction newMethod = cls.findMethodByName(PyNames.NEW, true, null); if (newMethod != null && !PyBuiltinCache.getInstance(call).isBuiltin(newMethod)) { return Ref.create(PyUnionType.createWeakType(new PyClassTypeImpl(cls, false))); diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index ec40da9ecaa1..a7f36e072bad 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -20,7 +20,6 @@ import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; @@ -265,7 +264,7 @@ public class PyFunctionImpl extends PyBaseElementImpl implements public PyType getCallType(@Nullable PyExpression receiver, @NotNull Map parameters, @NotNull TypeEvalContext context) { - return analyzeCallType(context.getReturnType(this), receiver, parameters, context); + return analyzeCallType(PyUtil.getReturnTypeToAnalyzeAsCallType(this, context), receiver, parameters, context); } @Nullable @@ -443,13 +442,6 @@ public class PyFunctionImpl extends PyBaseElementImpl implements } } - @Nullable - @Override - public PyType getReturnTypeFromDocString() { - final String typeName = extractReturnType(); - return typeName != null ? PyTypeParser.getTypeByName(this, typeName) : null; - } - @Nullable @Override public String getDeprecationMessage() { @@ -526,30 +518,6 @@ public class PyFunctionImpl extends PyBaseElementImpl implements return false; } - @Nullable - private String extractReturnType() { - final String ARROW = "->"; - final StructuredDocString structuredDocString = getStructuredDocString(); - if (structuredDocString != null) { - return structuredDocString.getReturnType(); - } - final String docString = getDocStringValue(); - if (docString != null && docString.contains(ARROW)) { - final List lines = StringUtil.split(docString, "\n"); - while (lines.size() > 0 && lines.get(0).trim().length() == 0) { - lines.remove(0); - } - if (lines.size() > 1 && lines.get(1).trim().length() == 0) { - String firstLine = lines.get(0); - int pos = firstLine.lastIndexOf(ARROW); - if (pos >= 0) { - return firstLine.substring(pos + 2).trim(); - } - } - } - return null; - } - private static class ReturnVisitor extends PyRecursiveElementVisitor { private final PyFunction myFunction; private final TypeEvalContext myContext; diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index ee98c6d753bf..995c1540703f 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -412,20 +412,28 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return null; } try { - return Collections.max(classTypes, (t1, t2) -> { - if (t1 == t2 || t1 != null && t1.equals(t2)) { - return 0; - } - else if (t2 == null || t1 != null && Sets.newHashSet(t1.getAncestorTypes(context)).contains(t2)) { - return 1; - } - else if (t1 == null || Sets.newHashSet(t2.getAncestorTypes(context)).contains(t1)) { - return -1; - } - else { - throw new NotDerivedClassTypeException(); - } - }); + final String abcMeta = "abc." + PyNames.ABC_META_CLASS; + + return classTypes + .stream() + .filter(t -> !abcMeta.equals(t.getClassQName())) + .max( + (t1, t2) -> { + if (Objects.equals(t1, t2)) { + return 0; + } + else if (t2 == null || t1 != null && Sets.newHashSet(t1.getAncestorTypes(context)).contains(t2)) { + return 1; + } + else if (t1 == null || Sets.newHashSet(t2.getAncestorTypes(context)).contains(t1)) { + return -1; + } + else { + throw new NotDerivedClassTypeException(); + } + } + ) + .orElse(null); } catch (NotDerivedClassTypeException ignored) { return null; diff --git a/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java b/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java index f763529ecb93..f3af243aa423 100644 --- a/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java +++ b/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java @@ -105,7 +105,7 @@ public class PyiTypeProvider extends PyTypeProviderBase { final List matchedReturnTypes = new ArrayList<>(); for (PyFunction overload : overloads) { - final PyType returnType = context.getReturnType(overload); + final PyType returnType = PyUtil.getReturnTypeToAnalyzeAsCallType(overload, context); allReturnTypes.add(PyTypeChecker.substitute(returnType, new HashMap<>(), context)); final PyCallExpression.PyArgumentsMapping mapping = PyCallExpressionHelper.mapArguments(callSite, overload, context); diff --git a/python/testData/inspections/PyMethodMayBeStaticInspection/documentedEmpty.py b/python/testData/inspections/PyMethodMayBeStaticInspection/documentedEmpty.py new file mode 100644 index 000000000000..55f88475538c --- /dev/null +++ b/python/testData/inspections/PyMethodMayBeStaticInspection/documentedEmpty.py @@ -0,0 +1,4 @@ +class A: + def foo(self): + """Do something""" + pass \ No newline at end of file diff --git a/python/testData/inspections/PyTypeCheckerInspection/AnnotatedDunderInitInGenericClass.py b/python/testData/inspections/PyTypeCheckerInspection/AnnotatedDunderInitInGenericClass.py new file mode 100644 index 000000000000..8fd3a0941734 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/AnnotatedDunderInitInGenericClass.py @@ -0,0 +1,6 @@ +from typing import TypeVar, Iterator + +T = TypeVar('T') +class MyIterator(Iterator[T]): + def __init__(self) -> None: + self.other = "other" \ No newline at end of file diff --git a/python/testData/inspections/PyTypeCheckerInspection/DunderInitAnnotatedAsNonNone.py b/python/testData/inspections/PyTypeCheckerInspection/DunderInitAnnotatedAsNonNone.py new file mode 100644 index 000000000000..68e9f305ab7b --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/DunderInitAnnotatedAsNonNone.py @@ -0,0 +1,9 @@ +class A: + def __init__(self) -> int: + pass + + +class B: + def __init__(self, foo): + # type: (str) -> int + pass \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/builtinListGetItem.py b/python/testData/inspections/PyUnresolvedReferencesInspection/builtinListGetItem.py index 5e9dbb73ed47..9c10c8424c45 100644 --- a/python/testData/inspections/PyUnresolvedReferencesInspection/builtinListGetItem.py +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/builtinListGetItem.py @@ -1,2 +1,2 @@ -x = list[int] +x = list[int] x = [][0] diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/memberFromMetaclassWhenSuperclassMetaclassIsABCMeta.py b/python/testData/inspections/PyUnresolvedReferencesInspection/memberFromMetaclassWhenSuperclassMetaclassIsABCMeta.py new file mode 100644 index 000000000000..82c403ba2b84 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/memberFromMetaclassWhenSuperclassMetaclassIsABCMeta.py @@ -0,0 +1,14 @@ +class MyMeta(type): + def __getitem__(self, item): + return 0 + + def foo(cls): + pass + + +class C(str, metaclass=MyMeta): + pass + + +print(C['foo']) +print(C.foo()) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 1c2006ee2a2d..268dedb2ec96 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -1964,6 +1964,15 @@ public class PyTypeTest extends PyTestCase { "expr = my_list.count"); } + public void testConstructingGenericClassWithNotFilledGenericValue() { + doTest("MyIterator", + "from typing import Iterator\n" + + "class MyIterator(Iterator[]):\n" + + " def __init__(self) -> None:\n" + + " self.other = \"other\"\n" + + "expr = MyIterator()"); + } + private static List getTypeEvalContexts(@NotNull PyExpression element) { return ImmutableList.of(TypeEvalContext.codeAnalysis(element.getProject(), element.getContainingFile()).withTracing(), TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile()).withTracing()); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyMethodMayBeStaticInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyMethodMayBeStaticInspectionTest.java index 1d33844e87e2..7a84199d2127 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyMethodMayBeStaticInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyMethodMayBeStaticInspectionTest.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. @@ -105,6 +105,11 @@ public class PyMethodMayBeStaticInspectionTest extends PyTestCase { runWithLanguageLevel(LanguageLevel.PYTHON30, () -> doTest()); } + // PY-24817 + public void testDocumentedEmpty() { + doTest(); + } + private void doTest() { myFixture.configureByFile(getTestName(true) + ".py"); myFixture.enableInspections(PyMethodMayBeStaticInspection.class); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java index 704fc4cf021c..caec7d0948be 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java @@ -422,4 +422,13 @@ public class PyTypeCheckerInspectionTest extends PyTestCase { public void testCallOperator() { runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); } + + // PY-24763 + public void testAnnotatedDunderInitInGenericClass() { + runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); + } + + public void testDunderInitAnnotatedAsNonNone() { + runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); + } } diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 0777b4ea01db..10a88d2360e7 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -603,6 +603,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase { doTest(); } + // PY-23540 + public void testMemberFromMetaclassWhenSuperclassMetaclassIsABCMeta() { + runWithLanguageLevel(LanguageLevel.PYTHON30, this::doTest); + } + // PY-23623 public void testCachedOperatorInRecursivelyTypeInference() { doTest(); diff --git a/resources-en/src/search/searchableOptions.xml b/resources-en/src/search/searchableOptions.xml index 1c513527ff5d..417b400d7b4b 100644 --- a/resources-en/src/search/searchableOptions.xml +++ b/resources-en/src/search/searchableOptions.xml @@ -9915,8 +9915,9 @@