diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java index 7c2a2df15a33..13ef8d37cd5c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -1769,6 +1769,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb ApplicationManager.getApplication().assertIsDispatchThread(); //TODO: remove this requirement ((XDebugSessionImpl)getXdebugProcess().getSession()).reset(); myState.set(STATE_INITIAL); + myRequestManager.processDetached(this, false); myConnection = environment.getRemoteConnection(); getManagerThread().restartIfNeeded(); createVirtualMachine(environment.getSessionName(), environment.isPollConnection()); diff --git a/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java b/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java index d4e529772a72..ac4c581f15e9 100644 --- a/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java +++ b/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java @@ -22,10 +22,11 @@ import com.intellij.execution.junit2.info.MethodLocation; import com.intellij.execution.testframework.sm.runner.SMTestLocator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiManager; import com.intellij.psi.PsiMethod; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.ClassUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -50,32 +51,27 @@ public class JavaTestLocator implements SMTestLocator { path = path.substring(0, idx); } - JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project); if (SUITE_PROTOCOL.equals(protocol)) { path = StringUtil.trimEnd(path, "."); - PsiClass[] classes = javaPsiFacade.findClasses(path, scope); - if (classes.length > 0) { + PsiClass aClass = ClassUtil.findPsiClass(PsiManager.getInstance(project), path, null, true, scope); + if (aClass != null) { results = ContainerUtil.newSmartList(); - for (PsiClass aClass : classes) { - results.add(paramName != null ? PsiMemberParameterizedLocation.getParameterizedLocation(aClass, paramName) - : new PsiLocation(project, aClass)); - } + results.add(paramName != null ? PsiMemberParameterizedLocation.getParameterizedLocation(aClass, paramName) + : new PsiLocation(project, aClass)); } } else if (TEST_PROTOCOL.equals(protocol)) { String className = StringUtil.getPackageName(path); if (!StringUtil.isEmpty(className)) { String methodName = StringUtil.getShortName(path); - PsiClass[] classes = javaPsiFacade.findClasses(className, scope); - if (classes.length > 0) { + PsiClass aClass = ClassUtil.findPsiClass(PsiManager.getInstance(project), className, null, true, scope); + if (aClass != null) { results = ContainerUtil.newSmartList(); - for (PsiClass aClass : classes) { - PsiMethod[] methods = aClass.findMethodsByName(methodName, true); - if (methods.length > 0) { - for (PsiMethod method : methods) { - results.add(paramName != null ? new PsiMemberParameterizedLocation(project, method, aClass, paramName) - : MethodLocation.elementInClass(method, aClass)); - } + PsiMethod[] methods = aClass.findMethodsByName(methodName, true); + if (methods.length > 0) { + for (PsiMethod method : methods) { + results.add(paramName != null ? new PsiMemberParameterizedLocation(project, method, aClass, paramName) + : MethodLocation.elementInClass(method, aClass)); } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionStatistician.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionStatistician.java index c5c1e3994a46..8fdcae303022 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionStatistician.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionStatistician.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.ExpectedTypeInfo; import com.intellij.codeInsight.ExpectedTypeInfoImpl; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupItem; +import com.intellij.patterns.ElementPattern; import com.intellij.psi.*; import com.intellij.psi.statistics.JavaStatisticsManager; import com.intellij.psi.statistics.StatisticsInfo; @@ -27,10 +28,13 @@ import com.intellij.util.containers.ContainerUtil; import java.util.List; +import static com.intellij.patterns.PsiJavaPatterns.psiElement; + /** * @author peter */ public class JavaCompletionStatistician extends CompletionStatistician{ + private static final ElementPattern SUPER_CALL = psiElement().afterLeaf(psiElement().withText(".").afterLeaf(PsiKeyword.SUPER)); @Override public StatisticsInfo serialize(final LookupElement element, final CompletionLocation location) { @@ -40,6 +44,10 @@ public class JavaCompletionStatistician extends CompletionStatistician{ return StatisticsInfo.EMPTY; } + if (SUPER_CALL.accepts(location.getCompletionParameters().getPosition())) { + return StatisticsInfo.EMPTY; + } + LookupItem item = element.as(LookupItem.CLASS_CONDITION_KEY); if (item == null) return null; diff --git a/java/java-psi-api/src/com/intellij/psi/util/JavaClassSupers.java b/java/java-psi-api/src/com/intellij/psi/util/JavaClassSupers.java new file mode 100644 index 000000000000..5acfb3cfb4a6 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/util/JavaClassSupers.java @@ -0,0 +1,50 @@ +/* + * 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.psi.util; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiSubstitutor; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author peter + */ +public abstract class JavaClassSupers { + + public static JavaClassSupers getInstance() { + return ServiceManager.getService(JavaClassSupers.class); + } + + /** + * Calculates substitutor that binds type parameters in superClass with + * values that they have in derivedClass, given that type parameters in + * derivedClass are bound by derivedSubstitutor. + * + * @return substitutor or null, if derivedClass doesn't inherit superClass + * @see PsiClass#isInheritor(PsiClass, boolean) + * @see InheritanceUtil#isInheritorOrSelf(PsiClass, PsiClass, boolean) + */ + @Nullable + public abstract PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, + @NotNull PsiClass derivedClass, + @NotNull GlobalSearchScope resolveScope, + @NotNull PsiSubstitutor derivedSubstitutor); + + +} diff --git a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java index 76fcac865c45..d4654337a9b6 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java @@ -830,7 +830,7 @@ public class TypeConversionUtil { && rText.endsWith(lText) && rText.charAt(rText.length() - lText.length() - 1) == '.'; } - return isClassAssignable(leftResult, rightResult, allowUncheckedConversion); + return isClassAssignable(leftResult, rightResult, allowUncheckedConversion, left.getResolveScope()); } private static boolean isAssignableFromWildcard(@NotNull PsiType left, @NotNull PsiWildcardType rightWildcardType) { @@ -923,18 +923,19 @@ public class TypeConversionUtil { private static boolean isClassAssignable(@NotNull PsiClassType.ClassResolveResult leftResult, @NotNull PsiClassType.ClassResolveResult rightResult, - boolean allowUncheckedConversion) { + boolean allowUncheckedConversion, GlobalSearchScope resolveScope) { final PsiClass leftClass = leftResult.getElement(); final PsiClass rightClass = rightResult.getElement(); - return leftClass != null - && rightClass != null - && InheritanceUtil.isInheritorOrSelf(rightClass, leftClass, true) - && typeParametersAgree(leftResult, rightResult, allowUncheckedConversion); + if (leftClass == null || rightClass == null) return false; + + PsiSubstitutor superSubstitutor = JavaClassSupers.getInstance().getSuperClassSubstitutor(leftClass, rightClass, resolveScope, + rightResult.getSubstitutor()); + return superSubstitutor != null && typeParametersAgree(leftResult, rightResult, allowUncheckedConversion, superSubstitutor); } private static boolean typeParametersAgree(@NotNull PsiClassType.ClassResolveResult leftResult, @NotNull PsiClassType.ClassResolveResult rightResult, - boolean allowUncheckedConversion) { + boolean allowUncheckedConversion, PsiSubstitutor superSubstitutor) { PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor(); PsiClass leftClass = leftResult.getElement(); PsiClass rightClass = rightResult.getElement(); @@ -945,7 +946,7 @@ public class TypeConversionUtil { PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor(); if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) { - rightSubstitutor = getSuperClassSubstitutor(leftClass, rightClass, rightSubstitutor); + rightSubstitutor = superSubstitutor; rightClass = leftClass; } else if (!PsiUtil.typeParametersIterator(rightClass).hasNext()) return true; @@ -1063,7 +1064,7 @@ public class TypeConversionUtil { * * @return substitutor (never returns null) * @see PsiClass#isInheritor(PsiClass, boolean) - * @see InheritanceUtil#isInheritorOrSelf(com.intellij.psi.PsiClass, com.intellij.psi.PsiClass, boolean) + * @see InheritanceUtil#isInheritorOrSelf(PsiClass, PsiClass, boolean) */ @NotNull public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/JavaClassSupersImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/JavaClassSupersImpl.java new file mode 100644 index 000000000000..4fed53fbd002 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/JavaClassSupersImpl.java @@ -0,0 +1,112 @@ +/* + * 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.psi.impl; + +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.JavaClassSupers; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; + +/** + * @author peter + */ +public class JavaClassSupersImpl extends JavaClassSupers { + + @Nullable + public PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, + @NotNull PsiClass derivedClass, + @NotNull GlobalSearchScope scope, + @NotNull PsiSubstitutor derivedSubstitutor) { + if (isJavaLangObject(superClass)) return PsiSubstitutor.EMPTY; + + return derivedClass instanceof PsiTypeParameter + ? processTypeParameter((PsiTypeParameter)derivedClass, scope, superClass, ContainerUtil.newTroveSet(), derivedSubstitutor) + : getSuperSubstitutorWithCaching(superClass, derivedClass, scope, derivedSubstitutor); + } + + @Nullable + private static PsiSubstitutor getSuperSubstitutorWithCaching(@NotNull PsiClass superClass, + @NotNull PsiClass derivedClass, + @NotNull GlobalSearchScope resolveScope, + @NotNull PsiSubstitutor derivedSubstitutor) { + PsiSubstitutor substitutor = ScopedClassHierarchy.getSuperClassSubstitutor(derivedClass, resolveScope, superClass); + if (substitutor == null) return null; + if (PsiUtil.isRawSubstitutor(derivedClass, derivedSubstitutor)) return createRawSubstitutor(superClass); + + return composeSubstitutors(derivedSubstitutor, substitutor); + } + + static boolean isJavaLangObject(PsiClass psiClass) { + return CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName()); + } + + @NotNull + static PsiSubstitutor createRawSubstitutor(@NotNull PsiClass superClass) { + return JavaPsiFacade.getElementFactory(superClass.getProject()).createRawSubstitutor(superClass); + } + + @NotNull + private static PsiSubstitutor composeSubstitutors(PsiSubstitutor outer, PsiSubstitutor inner) { + PsiSubstitutor answer = PsiSubstitutor.EMPTY; + for (Map.Entry entry : inner.getSubstitutionMap().entrySet()) { + answer = answer.put(entry.getKey(), outer.substitute(entry.getValue())); + } + return answer; + } + + /** + * Some type parameters (e.g. {@link com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable} change their supers at will, + * so caching the hierarchy is impossible. + */ + @Nullable + private static PsiSubstitutor processTypeParameter(PsiTypeParameter parameter, + GlobalSearchScope scope, + PsiClass superClass, + Set visited, + PsiSubstitutor derivedSubstitutor) { + if (parameter.getManager().areElementsEquivalent(parameter, superClass)) return PsiSubstitutor.EMPTY; + if (!visited.add(parameter)) return null; + + for (PsiClassType type : parameter.getExtendsListTypes()) { + PsiClassType.ClassResolveResult result = type.resolveGenerics(); + PsiClass psiClass = result.getElement(); + if (psiClass == null) continue; + + PsiSubstitutor answer; + if (psiClass instanceof PsiTypeParameter) { + answer = processTypeParameter((PsiTypeParameter)psiClass, scope, superClass, visited, derivedSubstitutor); + if (answer != null) { + return answer; + } + } + else { + answer = getSuperSubstitutorWithCaching(superClass, psiClass, scope, result.getSubstitutor()); + if (answer != null) { + return composeSubstitutors(derivedSubstitutor, answer); + } + } + } + + return null; + } + +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/ScopedClassHierarchy.java b/java/java-psi-impl/src/com/intellij/psi/impl/ScopedClassHierarchy.java new file mode 100644 index 000000000000..0145def28601 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/ScopedClassHierarchy.java @@ -0,0 +1,117 @@ +/* + * 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.psi.impl; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.*; +import com.intellij.util.containers.ConcurrentFactoryMap; +import com.intellij.util.containers.ContainerUtil; +import gnu.trove.TObjectHashingStrategy; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Iterator; +import java.util.Map; + +import static com.intellij.util.ObjectUtils.assertNotNull; + +/** + * @author peter + */ +class ScopedClassHierarchy { + private static final TObjectHashingStrategy CLASS_HASHING_STRATEGY = new TObjectHashingStrategy() { + @Override + public int computeHashCode(PsiClass object) { + return StringUtil.notNullize(object.getQualifiedName()).hashCode(); + } + + @Override + public boolean equals(PsiClass o1, PsiClass o2) { + return o1.getManager().areElementsEquivalent(o1, o2); + } + }; + private final Map mySupersWithSubstitutors = ContainerUtil.newTroveMap(CLASS_HASHING_STRATEGY); + + private ScopedClassHierarchy(PsiClass psiClass, GlobalSearchScope resolveScope) { + PsiClassType type = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass, PsiSubstitutor.EMPTY); + visitTypes(resolveScope, PsiSubstitutor.EMPTY, type); + } + + private void visitTypes(GlobalSearchScope resolveScope, PsiSubstitutor substitutor, PsiType... types) { + for (PsiType type : types) { + type = PsiClassImplUtil.correctType(substitutor.substitute(type), resolveScope); + if (!(type instanceof PsiClassType)) continue; + + PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics(); + PsiClass psiClass = resolveResult.getElement(); + if (psiClass == null || JavaClassSupersImpl.isJavaLangObject(psiClass) || mySupersWithSubstitutors.containsKey(psiClass)) continue; + + mySupersWithSubstitutors.put(psiClass, resolveResult); + + if (psiClass instanceof PsiAnonymousClass) { + visitTypes(resolveScope, resolveResult.getSubstitutor(), ((PsiAnonymousClass)psiClass).getBaseClassType()); + } + visitTypes(resolveScope, resolveResult.getSubstitutor(), psiClass.getExtendsListTypes()); + visitTypes(resolveScope, resolveResult.getSubstitutor(), psiClass.getImplementsListTypes()); + } + } + + @NotNull + private static ScopedClassHierarchy getHierarchy(@NotNull final PsiClass psiClass, @NotNull final GlobalSearchScope resolveScope) { + return CachedValuesManager.getCachedValue(psiClass, new CachedValueProvider>() { + @Nullable + @Override + public Result> compute() { + Map result = new ConcurrentFactoryMap() { + @Nullable + @Override + protected ScopedClassHierarchy create(GlobalSearchScope resolveScope) { + return new ScopedClassHierarchy(psiClass, resolveScope); + } + }; + return Result.create(result, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT); + } + }).get(resolveScope); + } + + @Nullable + static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass derivedClass, @NotNull GlobalSearchScope scope, @NotNull PsiClass superClass) { + PsiClassType.ClassResolveResult resolveResult = getHierarchy(derivedClass, scope).mySupersWithSubstitutors.get(superClass); + if (resolveResult == null) return null; + + PsiClass cachedClass = assertNotNull(resolveResult.getElement()); + PsiSubstitutor cachedSubstitutor = resolveResult.getSubstitutor(); + return cachedClass == superClass ? cachedSubstitutor : mirrorSubstitutor(superClass, cachedClass, cachedSubstitutor); + } + + @NotNull + private static PsiSubstitutor mirrorSubstitutor(@NotNull PsiClass from, @NotNull final PsiClass to, @NotNull PsiSubstitutor substitutor) { + Iterator baseParams = PsiUtil.typeParametersIterator(to); + Iterator candidateParams = PsiUtil.typeParametersIterator(from); + + PsiSubstitutor answer = PsiSubstitutor.EMPTY; + while (baseParams.hasNext()) { + // if equivalent classes "from" and "to" have different number of type parameters, then treat "to" as a raw type + if (!candidateParams.hasNext()) return JavaClassSupersImpl.createRawSubstitutor(to); + + answer = answer.put(baseParams.next(), substitutor.substitute(candidateParams.next())); + } + return answer; + } + +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java index 79242a72184f..0fa6744271df 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java @@ -15,10 +15,7 @@ */ package com.intellij.psi.impl.source.resolve.graphInference; -import com.intellij.psi.PsiClassType; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiType; -import com.intellij.psi.PsiTypeParameter; +import com.intellij.psi.*; import com.intellij.psi.impl.light.LightTypeParameter; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; @@ -133,6 +130,19 @@ public class InferenceVariable extends LightTypeParameter { myThrownBound = true; } + @Override + public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) { + for (PsiType type : getBounds(InferenceBound.UPPER)) { + PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(type); + if (psiClass != null) { + if (getManager().areElementsEquivalent(baseClass, psiClass)) return true; + if (checkDeep && psiClass.isInheritor(baseClass, true)) return true; + } + } + + return super.isInheritor(baseClass, checkDeep); + } + @Override public boolean isEquivalentTo(PsiElement another) { return this == another || getDelegate() == another; diff --git a/java/java-tests/testData/codeInsight/completion/normalSorting/NoStatsInSuperInvocation.java b/java/java-tests/testData/codeInsight/completion/normalSorting/NoStatsInSuperInvocation.java new file mode 100644 index 000000000000..a558e824c74c --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normalSorting/NoStatsInSuperInvocation.java @@ -0,0 +1,10 @@ +import java.lang.Object; +import java.lang.Override; +import java.util.HashMap; + +class Foo extends HashMap { + @Override + public Object get(Object key) { + pu + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java index 7a9220b850dc..5ec5e6c9425b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java @@ -16,7 +16,6 @@ package com.intellij.codeInsight; -import com.intellij.idea.Bombed; import com.intellij.openapi.application.ex.PathManagerEx; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ModifiableRootModel; @@ -30,8 +29,6 @@ import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.intellij.testFramework.fixtures.*; import com.intellij.util.Consumer; -import java.util.Calendar; - public class MultipleJdksHighlightingTest extends UsefulTestCase { private CodeInsightTestFixture myFixture; @@ -168,8 +165,8 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase { doTest3Modules(); } - @Bombed(user = "peter,anna", year = 2015, month = Calendar.MAY, day = 15) public void testCloseableAutoCloseable() { + IdeaTestUtil.setModuleLanguageLevel(myJava7Module, LanguageLevel.JDK_1_7); ModuleRootModificationUtil.addDependency(myJava7Module, myJava3Module); final String name = getTestName(false); myFixture.copyFileToProject("java3/p/" + name + ".java"); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy index d9b143677dbb..502acd69a68d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -39,6 +39,8 @@ import com.intellij.openapi.command.undo.UndoManager import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.EditorFactory import com.intellij.openapi.editor.actionSystem.EditorActionManager +import com.intellij.openapi.editor.event.DocumentAdapter +import com.intellij.openapi.editor.event.DocumentEvent import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.extensions.LoadingOrder @@ -152,10 +154,11 @@ class JavaAutoPopupTest extends CompletionAutoPopupTestCase { assertEquals 'iterable', lookup.currentItem.lookupString edt { myFixture.performEditorAction IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN } - assertEquals 'iterable2', lookup.currentItem.lookupString + assert lookup.currentItem.lookupString == 'iterable2' type "r" - myFixture.assertPreferredCompletionItems 2, "iter", "iterable", 'iterable2' + assert lookup.items[0].lookupString == 'iter' + assert lookup.currentItem.lookupString == 'iterable2' } @@ -950,6 +953,15 @@ class Foo { for (a1 in 0..actions) { for (a2 in 0..actions) { myFixture.configureByText("$a1 $a2 .java", src) + myFixture.editor.document.addDocumentListener(new DocumentAdapter() { + @Override + void documentChanged(DocumentEvent e) { + if (e.newFragment.toString().contains("a")) { + fail(e.toString()) + } + super.documentChanged(e) + } + }) myFixture.type 'i' joinSomething(a1) myFixture.type 'f' diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy index e0bc158b921b..b472f87f9c8d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy @@ -658,4 +658,16 @@ interface TxANotAnno {} assertPreferredItems 0, 'newLinkedSet1', 'newLinkedSet0', 'newLinkedSet2' } + public void testNoStatsInSuperInvocation() { + checkPreferredItems 0, 'put', 'putAll' + + myFixture.type('\n') + assert myFixture.editor.document.text.contains("put") + + myFixture.type(');\nsuper.') + myFixture.completeBasic() + + assertPreferredItems 0, 'get' + } + } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java index c8a8361b020b..a00ffe6c7f4d 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -286,7 +286,6 @@ public abstract class HTMLComposerImpl extends HTMLComposer { @Override public void appendListItem(StringBuffer buf, RefElement refElement) { startListItem(buf); - buf.append(CLOSE_TAG); appendElementReference(buf, refElement, true); appendAdditionalListItemInfo(buf, refElement); doneListItem(buf); diff --git a/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java b/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java index 6e313990e112..9dab9c62b3a3 100644 --- a/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java +++ b/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java @@ -58,7 +58,7 @@ public class BootstrapClassLoaderUtil extends ClassUtilCore { UrlClassLoader.Builder builder = UrlClassLoader.build() .urls(filterClassPath(new ArrayList(classpath))) - .allowLock(false) + .allowLock() .usePersistentClasspathIndexForLocalClassDirectories() .useCache(); if (Boolean.valueOf(System.getProperty(PROPERTY_ALLOW_BOOTSTRAP_RESOURCES, "true"))) { diff --git a/platform/core-api/src/com/intellij/util/IconUtil.java b/platform/core-api/src/com/intellij/util/IconUtil.java index 12c44dd12791..78c7c4d38752 100644 --- a/platform/core-api/src/com/intellij/util/IconUtil.java +++ b/platform/core-api/src/com/intellij/util/IconUtil.java @@ -446,6 +446,11 @@ public class IconUtil { } } + return createImageIcon(img); + } + + @NotNull + public static JBImageIcon createImageIcon(@NotNull final BufferedImage img) { return new JBImageIcon(img) { @Override public int getIconWidth() { diff --git a/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java b/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java index 49a400022a99..63aabc7bf446 100644 --- a/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java +++ b/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java @@ -21,9 +21,9 @@ import com.intellij.diff.chains.DiffRequestProducer; import com.intellij.diff.chains.DiffRequestProducerException; import com.intellij.diff.requests.*; import com.intellij.diff.tools.util.SoftHardCacheMap; +import com.intellij.diff.util.DiffTaskQueue; import com.intellij.diff.util.DiffUserDataKeys; import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy; -import com.intellij.diff.util.WaitingBackgroundableTaskExecutor; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -33,9 +33,11 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.util.Consumer; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.Convertor; +import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.CalledInBackground; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,7 +53,7 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso @NotNull private final SoftHardCacheMap myRequestCache = new SoftHardCacheMap(5, 5); - @NotNull private final WaitingBackgroundableTaskExecutor myTaskExecutor = new WaitingBackgroundableTaskExecutor(); + @NotNull private final DiffTaskQueue myQueue = new DiffTaskQueue(); public CacheDiffRequestChainProcessor(@Nullable Project project, @NotNull DiffRequestChain requestChain) { super(project, requestChain); @@ -62,6 +64,7 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso // Update // + @CalledInAwt public void updateRequest(final boolean force, @Nullable final ScrollToPolicy scrollToChangePolicy) { List requests = myRequestChain.getRequests(); int index = myRequestChain.getIndex(); @@ -78,12 +81,13 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso return; } - myTaskExecutor.execute( - new Convertor() { + myQueue.executeAndTryWait( + new Function() { @Override - public Runnable convert(ProgressIndicator indicator) { + public Runnable fun(ProgressIndicator indicator) { final DiffRequest request = loadRequest(producer, indicator); return new Runnable() { + @CalledInAwt @Override public void run() { myRequestCache.put(producer, request); @@ -131,9 +135,10 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso // @Override + @CalledInAwt protected void onDispose() { super.onDispose(); - myTaskExecutor.abort(); + Disposer.dispose(myQueue); myRequestCache.clear(); } diff --git a/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java b/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java index 2b6fbcb3a798..74674c4f83ab 100644 --- a/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java +++ b/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java @@ -299,6 +299,7 @@ public abstract class DiffRequestProcessor implements Disposable { protected void onAfterNavigate() { } + @CalledInAwt protected void onDispose() { } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java b/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java index 16dd6a0fd80c..8e03481e1c7a 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java @@ -20,7 +20,7 @@ import com.intellij.diff.FrameDiffTool; import com.intellij.diff.FrameDiffTool.DiffViewer; import com.intellij.diff.requests.ContentDiffRequest; import com.intellij.diff.tools.util.DiffDataKeys; -import com.intellij.diff.util.WaitingBackgroundableTaskExecutor; +import com.intellij.diff.util.DiffTaskQueue; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataProvider; @@ -31,8 +31,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; -import com.intellij.util.Alarm; -import com.intellij.util.containers.Convertor; +import com.intellij.util.Function; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.*; @@ -47,9 +46,7 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { @NotNull protected final DiffContext myContext; @NotNull protected final ContentDiffRequest myRequest; - @NotNull private final WaitingBackgroundableTaskExecutor myTaskExecutor = new WaitingBackgroundableTaskExecutor(); - @NotNull private final Alarm myAlarm = new Alarm(); - + @NotNull private final DiffTaskQueue myTaskExecutor = new DiffTaskQueue(); @NotNull private final AtomicBoolean myDisposed = new AtomicBoolean(false); public DiffViewerBase(@NotNull DiffContext context, @NotNull ContentDiffRequest request) { @@ -75,15 +72,11 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { public final void dispose() { if (!myDisposed.compareAndSet(false, true)) return; - Disposer.dispose(myAlarm); - abortRediff(); - onDispose(); UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { - abortRediff(); onDisposeAwt(); } }); @@ -92,14 +85,10 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { @CalledInAwt public final void scheduleRediff() { if (myDisposed.get()) return; - myTaskExecutor.abort(); - final int modificationStamp = myTaskExecutor.getModificationStamp(); - myAlarm.cancelAllRequests(); - myAlarm.addRequest(new Runnable() { + myTaskExecutor.abortAndSchedule(new Runnable() { @Override public void run() { - if (modificationStamp != myTaskExecutor.getModificationStamp()) return; rediff(); } }, ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS); @@ -127,10 +116,10 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { int waitMillis = trySync || tryRediffSynchronously() ? ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS : 0; - myTaskExecutor.execute( - new Convertor() { + myTaskExecutor.executeAndTryWait( + new Function() { @Override - public Runnable convert(ProgressIndicator indicator) { + public Runnable fun(ProgressIndicator indicator) { return performRediff(indicator); } }, @@ -199,6 +188,7 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { protected abstract Runnable performRediff(@NotNull ProgressIndicator indicator); protected void onDispose() { + Disposer.dispose(myTaskExecutor); } @CalledInAwt diff --git a/platform/diff-impl/src/com/intellij/diff/util/WaitingBackgroundableTaskExecutor.java b/platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java similarity index 63% rename from platform/diff-impl/src/com/intellij/diff/util/WaitingBackgroundableTaskExecutor.java rename to platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java index 3a95b5c60b9f..9cf267a06075 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/WaitingBackgroundableTaskExecutor.java +++ b/platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java @@ -21,7 +21,7 @@ import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.EmptyRunnable; -import com.intellij.util.containers.Convertor; +import com.intellij.util.Function; import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,59 +34,38 @@ import java.util.concurrent.atomic.AtomicReference; * Executor to perform long operations on pooled thread * Is is used to reduce blinking, in case of fast end of background task. */ -public class WaitingBackgroundableTaskExecutor { +public class BackgroundTaskUtil { private static final Runnable TOO_SLOW_OPERATION = new EmptyRunnable(); - private int myModificationStamp = 0; - @Nullable private ProgressIndicator myProgressIndicator; - @CalledInAwt - public int getModificationStamp() { - return myModificationStamp; + @NotNull + public static ProgressIndicator executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis) { + return executeAndTryWait(backgroundTask, onSlowAction, waitMillis, false); } @CalledInAwt - public void abort() { - if (myProgressIndicator != null) { - myProgressIndicator.cancel(); - myProgressIndicator = null; - myModificationStamp++; - } - } - - @CalledInAwt - public void execute(@NotNull final Convertor backgroundTask, - @Nullable final Runnable onSlowAction, - final int waitMillis) { - execute(backgroundTask, onSlowAction, waitMillis, false); - } - - @CalledInAwt - public void execute(@NotNull final Convertor backgroundTask, - @Nullable final Runnable onSlowAction, - final int waitMillis, - final boolean forceEDT) { - abort(); - - myModificationStamp++; - final int modificationStamp = myModificationStamp; - + @NotNull + public static ProgressIndicator executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis, + final boolean forceEDT) { final ModalityState modality = ModalityState.current(); - myProgressIndicator = new EmptyProgressIndicator() { + final ProgressIndicator indicator = new EmptyProgressIndicator() { @NotNull @Override public ModalityState getModalityState() { return modality; } }; - final ProgressIndicator indicator = myProgressIndicator; final Semaphore semaphore = new Semaphore(0); final AtomicReference resultRef = new AtomicReference(); if (forceEDT) { - Runnable result = backgroundTask.convert(indicator); - finish(result, modificationStamp, indicator); + Runnable callback = backgroundTask.fun(indicator); + finish(callback, indicator); } else { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @@ -95,18 +74,18 @@ public class WaitingBackgroundableTaskExecutor { ProgressManager.getInstance().executeProcessUnderProgress(new Runnable() { @Override public void run() { - final Runnable result = backgroundTask.convert(indicator); + final Runnable callback = backgroundTask.fun(indicator); if (indicator.isCanceled()) { semaphore.release(); return; } - if (!resultRef.compareAndSet(null, result)) { + if (!resultRef.compareAndSet(null, callback)) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - finish(result, modificationStamp, indicator); + finish(callback, indicator); } }, modality); } @@ -123,19 +102,20 @@ public class WaitingBackgroundableTaskExecutor { } if (!resultRef.compareAndSet(null, TOO_SLOW_OPERATION)) { // update presentation in the same thread to reduce blinking, caused by 'invokeLater' and fast background operation - finish(resultRef.get(), modificationStamp, indicator); + finish(resultRef.get(), indicator); } else { if (onSlowAction != null) onSlowAction.run(); } } + + return indicator; } @CalledInAwt - private void finish(@NotNull Runnable result, int modificationStamp, @NotNull ProgressIndicator indicator) { + private static void finish(@NotNull Runnable result, @NotNull ProgressIndicator indicator) { if (indicator.isCanceled()) return; - if (myModificationStamp != modificationStamp) return; - result.run(); + indicator.stop(); } } diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffTaskQueue.java b/platform/diff-impl/src/com/intellij/diff/util/DiffTaskQueue.java new file mode 100644 index 000000000000..96027e2a7356 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffTaskQueue.java @@ -0,0 +1,116 @@ +/* + * 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.diff.util; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.Alarm; +import com.intellij.util.Function; +import org.jetbrains.annotations.*; + +import java.util.concurrent.atomic.AtomicReference; + +public class DiffTaskQueue implements Disposable { + @NotNull private final Object LOCK = new Object(); + @NotNull private final Alarm myAlarm = new Alarm(); + + private boolean myDisposed; + @NotNull private final AtomicReference myProgressIndicator = new AtomicReference(); + + @CalledInAny + public void dispose() { + // if EDT is awaiting for background progress in executeAndTryWait - it holds LOCK. + // so we want to try cancel indicator before getting the lock. + cancelProgress(); + synchronized (LOCK) { + if (myDisposed) return; + myDisposed = true; + cancelProgress(); + Disposer.dispose(myAlarm); + } + } + + @CalledInAwt + public void abort() { + synchronized (LOCK) { + cancelProgress(); + myAlarm.cancelAllRequests(); + } + } + + private void cancelProgress() { + ProgressIndicator indicator = myProgressIndicator.getAndSet(null); + if (indicator != null) indicator.cancel(); + } + + @CalledInAwt + public void abortAndSchedule(@NotNull final Runnable task, int millis) { + synchronized (LOCK) { + if (myDisposed) return; + abort(); + + myAlarm.addRequest(new Runnable() { + @Override + public void run() { + synchronized (LOCK) { + if (myDisposed) return; + } + task.run(); + } + }, millis); + } + } + + @CalledInAwt + public void executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis) { + executeAndTryWait(backgroundTask, onSlowAction, waitMillis, false); + } + + @CalledInAwt + public void executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis, + final boolean forceEDT) { + synchronized (LOCK) { + if (myDisposed) return; + abort(); + + Function function = new Function() { + @Override + @CalledInBackground + public Runnable fun(final ProgressIndicator indicator) { + final Runnable callback = backgroundTask.fun(indicator); + return new Runnable() { + @Override + @CalledInAwt + public void run() { + synchronized (LOCK) { + if (myDisposed) return; + indicator.checkCanceled(); + } + callback.run(); + } + }; + } + }; + + myProgressIndicator.set(BackgroundTaskUtil.executeAndTryWait(function, onSlowAction, waitMillis, forceEDT)); + } + } +} diff --git a/platform/dvcs-api/src/com/intellij/dvcs/repo/RepositoryManager.java b/platform/dvcs-api/src/com/intellij/dvcs/repo/RepositoryManager.java index 2292220e6672..7c91d458f622 100644 --- a/platform/dvcs-api/src/com/intellij/dvcs/repo/RepositoryManager.java +++ b/platform/dvcs-api/src/com/intellij/dvcs/repo/RepositoryManager.java @@ -47,9 +47,6 @@ public interface RepositoryManager { @Nullable T getRepositoryForFile(@NotNull VirtualFile file); - @Nullable - T getRepositoryForFile(@NotNull VirtualFile file, boolean updateIfNeeded); - /** * Returns the {@link Repository} which the given file belongs to, or {@code null} if the file is not under any Git ot Hg repository. */ diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/repo/AbstractRepositoryManager.java b/platform/dvcs-impl/src/com/intellij/dvcs/repo/AbstractRepositoryManager.java index 6a0d2fce2fe8..69a6e9afc41d 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/repo/AbstractRepositoryManager.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/repo/AbstractRepositoryManager.java @@ -29,9 +29,12 @@ public abstract class AbstractRepositoryManager @Override @Nullable public T getRepositoryForRoot(@Nullable VirtualFile root) { - Repository repository = myGlobalRepositoryManager.getRepositoryForRoot(root, true); - //noinspection unchecked - return isRootValid(repository) ? (T)repository : null; + return validateAndGetRepository(myGlobalRepositoryManager.getRepositoryForRoot(root)); + } + + @Nullable + public T getRepositoryForRootQuick(@Nullable VirtualFile root) { + return validateAndGetRepository(myGlobalRepositoryManager.getRepositoryForRootQuick(root)); } @Override @@ -52,15 +55,7 @@ public abstract class AbstractRepositoryManager @Override @Nullable public T getRepositoryForFile(@NotNull VirtualFile file) { - return getRepositoryForFile(file, true); - } - - @Override - @Nullable - public T getRepositoryForFile(@NotNull VirtualFile file, boolean updateIfNeeded) { - Repository repository = myGlobalRepositoryManager.getRepositoryForFile(file, updateIfNeeded); - //noinspection unchecked - return repository != null && myVcs.equals(repository.getVcs()) ? (T)repository : null; + return validateAndGetRepository(myGlobalRepositoryManager.getRepositoryForFile(file)); } @Override @@ -103,9 +98,11 @@ public abstract class AbstractRepositoryManager }); } - private boolean isRootValid(@Nullable Repository repository) { - if (repository == null || !myVcs.equals(repository.getVcs())) return false; + @Nullable + private T validateAndGetRepository(@Nullable Repository repository) { + if (repository == null || !myVcs.equals(repository.getVcs())) return null; VirtualFile vcsDir = repository.getRoot().findChild(myRepoDirName); - return vcsDir != null && vcsDir.exists(); + //noinspection unchecked + return vcsDir != null && vcsDir.exists() ? (T)repository : null; } } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/repo/VcsRepositoryManager.java b/platform/dvcs-impl/src/com/intellij/dvcs/repo/VcsRepositoryManager.java index c55cac8d7357..ba27ce4d2b19 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/repo/VcsRepositoryManager.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/repo/VcsRepositoryManager.java @@ -80,17 +80,22 @@ public class VcsRepositoryManager extends AbstractProjectComponent implements Di @Nullable public Repository getRepositoryForFile(@NotNull VirtualFile file) { - return getRepositoryForFile(file, true); - } - - @Nullable - public Repository getRepositoryForFile(@NotNull VirtualFile file, boolean updateIfNeeded) { final VcsRoot vcsRoot = myVcsManager.getVcsRootObjectFor(file); - return vcsRoot != null ? getRepositoryForRoot(vcsRoot.getPath(), updateIfNeeded) : null; + return vcsRoot != null ? getRepositoryForRoot(vcsRoot.getPath()) : null; } @Nullable - public Repository getRepositoryForRoot(@Nullable VirtualFile root, boolean updateIfNeeded) { + public Repository getRepositoryForRootQuick(@Nullable VirtualFile root) { + return getRepositoryForRoot(root, false); + } + + @Nullable + public Repository getRepositoryForRoot(@Nullable VirtualFile root) { + return getRepositoryForRoot(root, true); + } + + @Nullable + private Repository getRepositoryForRoot(@Nullable VirtualFile root, boolean updateIfNeeded) { if (root == null) return null; Repository result; try { diff --git a/platform/dvcs-impl/testSrc/com.intellij.dvcs/VcsRepositoryManagerTest.java b/platform/dvcs-impl/testSrc/com.intellij.dvcs/VcsRepositoryManagerTest.java index f2519070ae5d..022a644570ce 100644 --- a/platform/dvcs-impl/testSrc/com.intellij.dvcs/VcsRepositoryManagerTest.java +++ b/platform/dvcs-impl/testSrc/com.intellij.dvcs/VcsRepositoryManagerTest.java @@ -120,12 +120,12 @@ public class VcsRepositoryManagerTest extends UsefulTestCase { public void testRepositoryInfoReadingWhileModifying() throws Exception { final VirtualFile repositoryFile = createExternalRepository(); - assertNotNull(myGlobalRepositoryManager.getRepositoryForRoot(repositoryFile, true)); + assertNotNull(myGlobalRepositoryManager.getRepositoryForRoot(repositoryFile)); FutureTask readExistingRepo = new FutureTask(new Callable() { @Override public Repository call() throws Exception { - return myGlobalRepositoryManager.getRepositoryForRoot(repositoryFile, true); + return myGlobalRepositoryManager.getRepositoryForRoot(repositoryFile); } }); diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/ui/views/HistoryDialog.java b/platform/lvcs-impl/src/com/intellij/history/integration/ui/views/HistoryDialog.java index b4d9521b00b0..4c909aeeb01b 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/ui/views/HistoryDialog.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/ui/views/HistoryDialog.java @@ -50,6 +50,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.patch.CreatePatchConfigurationPanel; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy; import com.intellij.ui.*; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.components.JBLayeredPane; @@ -494,9 +495,10 @@ public abstract class HistoryDialog extends FrameW private boolean showAsDialog(CreatePatchConfigurationPanel p) { final DialogBuilder b = new DialogBuilder(myProject); - b.setPreferredFocusComponent(p.getPreferredFocusComponent()); + JComponent createPatchPanel = p.getPanel(); + b.setPreferredFocusComponent(IdeFocusTraversalPolicy.getPreferredFocusedComponent(createPatchPanel)); b.setTitle(message("create.patch.dialog.title")); - b.setCenterPanel(p.getPanel()); + b.setCenterPanel(createPatchPanel); p.installOkEnabledListener(new Consumer() { public void consume(final Boolean aBoolean) { b.setOkActionEnabled(aBoolean); diff --git a/platform/platform-api/src/com/intellij/ui/treeStructure/treetable/TreeTableCellRenderer.java b/platform/platform-api/src/com/intellij/ui/treeStructure/treetable/TreeTableCellRenderer.java index 19e12595de77..1bd5f1d303e2 100644 --- a/platform/platform-api/src/com/intellij/ui/treeStructure/treetable/TreeTableCellRenderer.java +++ b/platform/platform-api/src/com/intellij/ui/treeStructure/treetable/TreeTableCellRenderer.java @@ -41,14 +41,15 @@ public class TreeTableCellRenderer implements TableCellRenderer, ClientPropertyH public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { int modelRow = table.convertRowIndexToModel(row); + final boolean lineHasFocus = table.hasFocus(); if (myTreeCellRenderer != null) myTree.setCellRenderer(myTreeCellRenderer); if (isSelected){ - myTree.setBackground(table.getSelectionBackground()); + myTree.setBackground(lineHasFocus ? table.getSelectionBackground() : UIUtil.getTreeUnfocusedSelectionBackground()); myTree.setForeground(table.getSelectionForeground()); } - else{ + else { myTree.setBackground(table.getBackground()); myTree.setForeground(table.getForeground()); } @@ -60,7 +61,7 @@ public class TreeTableCellRenderer implements TableCellRenderer, ClientPropertyH final Object treeObject = myTree.getPathForRow(modelRow).getLastPathComponent(); boolean leaf = myTree.getModel().isLeaf(treeObject); final boolean expanded = myTree.isExpanded(modelRow); - Component component = myTree.getCellRenderer().getTreeCellRendererComponent(myTree, treeObject, isSelected, expanded, leaf, modelRow, hasFocus); + Component component = myTree.getCellRenderer().getTreeCellRendererComponent(myTree, treeObject, isSelected, expanded, leaf, modelRow, lineHasFocus); if (component instanceof JComponent) { table.setToolTipText(((JComponent)component).getToolTipText()); } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java index 7516febe596b..c5b459261fa8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -556,7 +556,7 @@ public class FileWatcher { case CREATE: case DELETE: for (String p : paths) { - myDirtyPaths.dirtyPaths.add(p); + myDirtyPaths.dirtyPathsRecursive.add(p); String parentPath = new File(p).getParent(); if (parentPath != null) { myDirtyPaths.dirtyPaths.add(parentPath); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java index 8baa5a78c50f..7f52a1df5ff6 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java @@ -714,6 +714,33 @@ public class FileWatcherTest extends PlatformLangTestCase { } } + public void testDisplacementByIsomorphicTree() throws Exception { + File top = createTestDir("top"); + File up = createTestDir(top, "up"); + File middle = createTestDir(up, "middle"); + File file = createTestFile(middle, "file.txt", "original content"); + File up_copy = new File(top, "up_copy"); + FileUtil.copyDir(up, up_copy); + FileUtil.writeToFile(file, "new content"); + + VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + assertNotNull(vFile); + assertEquals("new content", VfsUtilCore.loadText(vFile)); + + LocalFileSystem.WatchRequest request = watch(up); + try { + myAccept = true; + FileUtil.rename(up, new File(top, "up.bak")); + FileUtil.rename(up_copy, up); + assertEvent(VFileContentChangeEvent.class, file.getPath()); + assertTrue(vFile.isValid()); + assertEquals("original content", VfsUtilCore.loadText(vFile)); + } + finally { + unwatch(request); + } + } + @NotNull private LocalFileSystem.WatchRequest watch(File watchFile) { diff --git a/platform/testFramework/src/com/intellij/testFramework/CompositeException.java b/platform/testFramework/src/com/intellij/testFramework/CompositeException.java index 59e73bf718d4..4ec1c8b21107 100644 --- a/platform/testFramework/src/com/intellij/testFramework/CompositeException.java +++ b/platform/testFramework/src/com/intellij/testFramework/CompositeException.java @@ -130,7 +130,8 @@ public class CompositeException extends Exception { sb.append(line); line = exceptionProcessor.fun(exception); - if (!line.endsWith("\n")) line += '\n'; + if (line == null) line = "null\n"; + else if (!line.endsWith("\n")) line += '\n'; stringProcessor.process(line); sb.append(line); } diff --git a/platform/testFramework/testFramework.iml b/platform/testFramework/testFramework.iml index 19a4097b1f15..bb99b4dbda56 100644 --- a/platform/testFramework/testFramework.iml +++ b/platform/testFramework/testFramework.iml @@ -14,12 +14,12 @@ - + - + @@ -27,5 +27,4 @@ - - + \ No newline at end of file diff --git a/platform/util/src/com/intellij/util/diff/Diff.java b/platform/util/src/com/intellij/util/diff/Diff.java index f6ee59c5dbef..2b253e471212 100644 --- a/platform/util/src/com/intellij/util/diff/Diff.java +++ b/platform/util/src/com/intellij/util/diff/Diff.java @@ -80,9 +80,17 @@ public class Diff { changes = patienceIntLCS.getChanges(); } else { - IntLCS intLCS = new IntLCS(discarded[0], discarded[1]); - intLCS.execute(); - changes = intLCS.getChanges(); + try { + IntLCS intLCS = new IntLCS(discarded[0], discarded[1]); + intLCS.execute(); + changes = intLCS.getChanges(); + } + catch (FilesTooBigForDiffException e) { + PatienceIntLCS patienceIntLCS = new PatienceIntLCS(discarded[0], discarded[1]); + patienceIntLCS.failOnSmallSizeReduction(); + patienceIntLCS.execute(); + changes = patienceIntLCS.getChanges(); + } } reindexer.reindex(changes, builder); diff --git a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java index f20353e48ccf..947f144e8b3a 100644 --- a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java +++ b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java @@ -29,6 +29,8 @@ public class PatienceIntLCS { private final BitSet myChanges1; private final BitSet myChanges2; + private boolean myFailOnSmallReduction; + public PatienceIntLCS(int[] first, int[] second) { this(first, second, 0, first.length, 0, second.length, new BitSet(first.length), new BitSet(second.length)); } @@ -45,6 +47,10 @@ public class PatienceIntLCS { myChanges2 = changes2; } + public void failOnSmallSizeReduction() { + myFailOnSmallReduction = true; + } + public void execute() throws FilesTooBigForDiffException { if (myCount1 == 0 && myCount2 == 0) { return; @@ -71,6 +77,7 @@ public class PatienceIntLCS { int[][] matching = uniqueLCS.execute(); if (matching == null) { + checkReduction(count1, count2); IntLCS intLCS = new IntLCS(myFirst, mySecond, start1, count1, start2, count2, myChanges1, myChanges2); intLCS.execute(); } @@ -79,8 +86,12 @@ public class PatienceIntLCS { int matched = matching[0].length; assert matched > 0; + c1 = matching[0][0]; + c2 = matching[1][0]; + + checkReduction(c1, c2); PatienceIntLCS patienceDiff = - new PatienceIntLCS(myFirst, mySecond, start1, matching[0][0], start2, matching[1][0], myChanges1, myChanges2); + new PatienceIntLCS(myFirst, mySecond, start1, c1, start2, c2, myChanges1, myChanges2); patienceDiff.execute(); for (int i = 1; i < matching[0].length; i++) { @@ -91,6 +102,7 @@ public class PatienceIntLCS { c2 = matching[1][i] - s2; if (c1 > 0 || c2 > 0) { + checkReduction(c1, c2); patienceDiff = new PatienceIntLCS(myFirst, mySecond, start1 + s1, c1, start2 + s2, c2, myChanges1, myChanges2); patienceDiff.execute(); } @@ -113,6 +125,7 @@ public class PatienceIntLCS { c2 = count2 - s2; } + checkReduction(c1, c2); patienceDiff = new PatienceIntLCS(myFirst, mySecond, start1 + s1, c1, start2 + s2, c2, myChanges1, myChanges2); patienceDiff.execute(); } @@ -147,4 +160,11 @@ public class PatienceIntLCS { public BitSet[] getChanges() { return new BitSet[]{myChanges1, myChanges2}; } + + private void checkReduction(int count1, int count2) throws FilesTooBigForDiffException { + if (!myFailOnSmallReduction) return; + if (count1 * 2 < myCount1) return; + if (count2 * 2 < myCount2) return; + throw new FilesTooBigForDiffException(0); + } } diff --git a/platform/util/src/com/intellij/util/lang/JarLoader.java b/platform/util/src/com/intellij/util/lang/JarLoader.java index 68c0bfb5479e..17b8181c619b 100644 --- a/platform/util/src/com/intellij/util/lang/JarLoader.java +++ b/platform/util/src/com/intellij/util/lang/JarLoader.java @@ -17,11 +17,11 @@ package com.intellij.util.lang; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.io.URLUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import sun.misc.Resource; +import java.io.File; import java.io.IOException; import java.lang.ref.SoftReference; import java.net.URL; @@ -30,15 +30,18 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; class JarLoader extends Loader { - private final URL myURL; + private final File myCanonicalFile; + private final boolean myCanLockJar; // true implies that the zipfile will not be modified in the lifetime of the JarLoader private SoftReference myMemoryLoader; + private volatile SoftReference myZipFileSoftReference; // Used only when myCanLockJar==true - // todo drop unused parameter JarLoader(URL url, @SuppressWarnings("unused") boolean canLockJar, int index, boolean preloadJarContents) throws IOException { - super(new URL(URLUtil.JAR_PROTOCOL, "", -1, url + "!/"), index); - myURL = url; + super(new URL("jar", "", -1, url + "!/"), index); - ZipFile zipFile = new ZipFile(getFileUrl()); + myCanonicalFile = new File(FileUtil.unquote(url.getFile())).getCanonicalFile(); + myCanLockJar = canLockJar; + + ZipFile zipFile = getZipFile(); // IOException from opening is propagated to caller if zip file isn't valid, try { if (preloadJarContents) { JarMemoryLoader loader = JarMemoryLoader.load(zipFile, getBaseURL()); @@ -48,18 +51,14 @@ class JarLoader extends Loader { } } finally { - zipFile.close(); + releaseZipFile(zipFile); } } - private String getFileUrl() throws IOException { - return FileUtil.unquote(myURL.getFile()); - } - @NotNull @Override public ClasspathCache.LoaderData buildData() throws IOException { - ZipFile zipFile = new ZipFile(getFileUrl()); + ZipFile zipFile = getZipFile(); try { ClasspathCache.LoaderData loaderData = new ClasspathCache.LoaderData(); Enumeration entries = zipFile.entries(); @@ -72,21 +71,21 @@ class JarLoader extends Loader { return loaderData; } finally { - zipFile.close(); + releaseZipFile(zipFile); } } @Override @Nullable Resource getResource(String name, boolean flag) { - JarMemoryLoader loader = com.intellij.reference.SoftReference.dereference(myMemoryLoader); + JarMemoryLoader loader = myMemoryLoader != null? myMemoryLoader.get() : null; if (loader != null) { Resource resource = loader.getResource(name); if (resource != null) return resource; } try { - ZipFile zipFile = new ZipFile(getFileUrl()); + ZipFile zipFile = getZipFile(); try { ZipEntry entry = zipFile.getEntry(name); if (entry != null) { @@ -94,18 +93,58 @@ class JarLoader extends Loader { } } finally { - zipFile.close(); + releaseZipFile(zipFile); } } catch (Exception e) { - Logger.getInstance(JarLoader.class).error("url: " + myURL, e); + error("file: " + myCanonicalFile, e); } return null; } + protected void error(String message, Throwable t) { + //Logger.getLogger(JarLoader.class.getName()).log(Level.SEVERE, message, t); + Logger.getInstance(JarLoader.class).error(message, t); + } + + private void releaseZipFile(ZipFile zipFile) throws IOException { + // Closing of zip file when myCanLockJar=true happens in ZipFile.finalize + if (!myCanLockJar) { + zipFile.close(); + } + } + + @NotNull + private ZipFile getZipFile() throws IOException { + // This code is executed at least 100K times (O(number of classes needed to load)) and it takes considerable time to open ZipFile's + // such number of times so we store reference to ZipFile if we allowed to lock the file (assume it isn't changed) + if (myCanLockJar) { + SoftReference zipFileSoftReference = myZipFileSoftReference; + if(zipFileSoftReference != null) { + ZipFile existingZipFile = zipFileSoftReference.get(); + if (existingZipFile != null) return existingZipFile; + } + synchronized (ourLock) { + zipFileSoftReference = myZipFileSoftReference; + if(zipFileSoftReference != null) { + ZipFile existingZipFile = zipFileSoftReference.get(); + if (existingZipFile != null) return existingZipFile; + } + // ZipFile's native implementation (ZipFile.c, zip_util.c) has path -> file descriptor cache + ZipFile zipFile = new ZipFile(myCanonicalFile); + myZipFileSoftReference = new SoftReference(zipFile); + return zipFile; + } + } else { + return new ZipFile(myCanonicalFile); + } + } + @Override public String toString() { - return "JarLoader [" + myURL + "]"; + return "JarLoader [" + myCanonicalFile + "]"; } + + private static final Object ourLock = new Object(); } diff --git a/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java b/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java index f012f1fc6526..497db669ba7f 100644 --- a/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java +++ b/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java @@ -15,9 +15,7 @@ */ package com.intellij.util.lang; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.containers.hash.HashMap; -import com.intellij.util.io.zip.ZipShort; +import com.intellij.openapi.util.io.FileUtilRt; import org.jetbrains.annotations.Nullable; import sun.misc.Resource; @@ -26,6 +24,7 @@ import java.io.IOException; import java.net.URL; import java.util.Collections; import java.util.Enumeration; +import java.util.HashMap; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -64,8 +63,8 @@ public class JarMemoryLoader { ZipEntry sizeEntry = entries.nextElement(); if (sizeEntry == null || !sizeEntry.getName().equals(SIZE_ENTRY)) return null; - byte[] bytes = FileUtil.loadBytes(zipFile.getInputStream(sizeEntry), 2); - int size = ZipShort.getValue(bytes); + byte[] bytes = FileUtilRt.loadBytes(zipFile.getInputStream(sizeEntry), 2); + int size = ((bytes[1] & 0xFF) << 8) + (bytes[0] & 0xFF); JarMemoryLoader loader = new JarMemoryLoader(); for (int i = 0; i < size && entries.hasMoreElements(); i++) { diff --git a/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java b/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java index 93cdf6a01822..e2e1f74c3690 100644 --- a/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java +++ b/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java @@ -38,6 +38,7 @@ import java.awt.event.MouseListener; */ public class WideSelectionTreeUI extends BasicTreeUI { public static final String TREE_TABLE_TREE_KEY = "TreeTableTree"; + public static final String NO_SELECTION_PAINTING_DARK = "NO_SELECTION_PAINTING_DARK"; @NonNls public static final String SOURCE_LIST_CLIENT_PROPERTY = "mac.ui.source.list"; @NonNls public static final String STRIPED_CLIENT_PROPERTY = "mac.ui.striped"; @@ -361,7 +362,8 @@ public class WideSelectionTreeUI extends BasicTreeUI { } } else { - if (selected && (UIUtil.isUnderAquaBasedLookAndFeel() || UIUtil.isUnderDarcula() || UIUtil.isUnderIntelliJLaF())) { + if (selected && (UIUtil.isUnderAquaBasedLookAndFeel() || UIUtil.isUnderDarcula() || UIUtil.isUnderIntelliJLaF()) + && ! Boolean.TRUE.equals(tree.getClientProperty(NO_SELECTION_PAINTING_DARK))) { Color bg = UIUtil.getTreeSelectionBackground(tree.hasFocus() || Boolean.TRUE.equals(tree.getClientProperty(TREE_TABLE_TREE_KEY))); if (myWideSelectionCondition.value(row)) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java index 2df8cad37e62..fd0b781bd06a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java @@ -19,9 +19,9 @@ import com.intellij.diff.chains.DiffRequestProducerException; import com.intellij.diff.impl.DiffRequestProcessor; import com.intellij.diff.requests.*; import com.intellij.diff.tools.util.SoftHardCacheMap; +import com.intellij.diff.util.DiffTaskQueue; import com.intellij.diff.util.DiffUserDataKeys; import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy; -import com.intellij.diff.util.WaitingBackgroundableTaskExecutor; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -31,10 +31,11 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.changes.actions.diff.ChangeDiffRequestProducer; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.Convertor; import org.jetbrains.annotations.*; import java.util.Collections; @@ -48,7 +49,7 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { @Nullable private Change myCurrentChange; - @NotNull private final WaitingBackgroundableTaskExecutor myTaskExecutor = new WaitingBackgroundableTaskExecutor(); + @NotNull private final DiffTaskQueue myQueue = new DiffTaskQueue(); public CacheChangeProcessor(@NotNull Project project) { super(project); @@ -74,21 +75,25 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { // Update // + @CalledInAwt public void updateRequest(final boolean force, @Nullable final ScrollToPolicy scrollToChangePolicy) { final Change change = myCurrentChange; + DiffRequest cachedRequest = loadRequestFast(change); if (cachedRequest != null) { applyRequest(cachedRequest, force, scrollToChangePolicy); return; } - myTaskExecutor.execute( - new Convertor() { + // TODO: check if current loading change is the same as we want to load now? (and not interrupt loading) + myQueue.executeAndTryWait( + new Function() { @Override - public Runnable convert(ProgressIndicator indicator) { + public Runnable fun(ProgressIndicator indicator) { final DiffRequest request = loadRequest(change, indicator); return new Runnable() { @Override + @CalledInAwt public void run() { myRequestCache.put(change, Pair.create(change, request)); applyRequest(request, force, scrollToChangePolicy); @@ -107,6 +112,7 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { } @Nullable + @CalledInAwt @Contract("null -> !null") protected DiffRequest loadRequestFast(@Nullable Change change) { if (change == null) return NoDiffRequest.INSTANCE; @@ -152,9 +158,10 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { // @Override + @CalledInAwt protected void onDispose() { super.onDispose(); - myTaskExecutor.abort(); + Disposer.dispose(myQueue); myRequestCache.clear(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchConfigurationPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchConfigurationPanel.java index 2a98132bbbdb..c398b5fbd62c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchConfigurationPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchConfigurationPanel.java @@ -34,7 +34,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.VcsConfiguration; import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.ui.ComplexFocusedComponentWrapper; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -235,20 +234,8 @@ public class CreatePatchConfigurationPanel { return myIncludedChanges; } - @NotNull public JComponent getPanel() { - return new ComplexFocusedComponentWrapper( - !myIncludeBaseRevisionTextCheckBox.isVisible() || myChanges.isEmpty() ? myMainPanel : myPanelWithSelectedFiles) { - @Override - public JComponent getPreferredFocusedSimpleComponent() { - return getPreferredFocusComponent(); - } - }; - } - - @NotNull - public JComponent getPreferredFocusComponent() { - return myFileNameField; + return !myIncludeBaseRevisionTextCheckBox.isVisible() || myChanges.isEmpty() ? myMainPanel : myPanelWithSelectedFiles; } public void installOkEnabledListener(final Consumer runnable) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ComplexFocusedComponentWrapper.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ComplexFocusedComponentWrapper.java deleted file mode 100644 index dc287817654b..000000000000 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ComplexFocusedComponentWrapper.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.vcs.changes.ui; - -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.awt.*; - -public class ComplexFocusedComponentWrapper extends JPanel { - - public ComplexFocusedComponentWrapper(@NotNull JComponent component) { - super(new BorderLayout()); - add(component); - } - - public JComponent getPreferredFocusedSimpleComponent() { - //return all panel by default - return this; - } -} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SessionDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SessionDialog.java index 5980ea0635f2..fccbcf69af90 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SessionDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SessionDialog.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.CommitSession; +import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy; import com.intellij.util.Alarm; import org.jetbrains.annotations.Nullable; @@ -68,8 +69,7 @@ public class SessionDialog extends DialogWrapper { @Override public JComponent getPreferredFocusedComponent() { - return myConfigurationComponent instanceof ComplexFocusedComponentWrapper ? - ((ComplexFocusedComponentWrapper)myConfigurationComponent).getPreferredFocusedSimpleComponent() : myConfigurationComponent; + return IdeFocusTraversalPolicy.getPreferredFocusedComponent(myConfigurationComponent); } private void updateButtons() { diff --git a/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/ExternalChangesDetectionVcsTest.java b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/ExternalChangesDetectionVcsTest.java new file mode 100644 index 000000000000..ece7f752ddc7 --- /dev/null +++ b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/ExternalChangesDetectionVcsTest.java @@ -0,0 +1,211 @@ +package com.intellij.openapi.vcs.changes.committed; + +import com.intellij.ide.startup.impl.StartupManagerImpl; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TempDirTestFixture; +import com.intellij.testFramework.vcs.AbstractJunitVcsTestCase; +import com.intellij.util.Processor; +import com.intellij.util.ui.UIUtil; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.List; +import java.util.regex.Pattern; + +/** + * @author irengrig + */ +public class ExternalChangesDetectionVcsTest extends AbstractJunitVcsTestCase { + private MockAbstractVcs myVcs; + private ProjectLevelVcsManagerImpl myVcsManager; + private LocalFileSystem myLFS; + private ChangeListManager myChangeListManager; + private VcsDirtyScopeManager myVcsDirtyScopeManager; + private TempDirTestFixture myTempDirTestFixture; + private File myClientRoot; + + @Before + public void setUp() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory(); + myTempDirTestFixture = fixtureFactory.createTempDirTestFixture(); + myTempDirTestFixture.setUp(); + + myClientRoot = new File(myTempDirTestFixture.getTempDirPath(), "clientroot"); + myClientRoot.mkdir(); + + initProject(myClientRoot, ExternalChangesDetectionVcsTest.this.getTestName()); + + ((StartupManagerImpl)StartupManager.getInstance(myProject)).runPostStartupActivities(); + + myVcs = new MockAbstractVcs(myProject); + myVcs.setChangeProvider(new MyMockChangeProvider()); + myVcsManager = (ProjectLevelVcsManagerImpl)ProjectLevelVcsManager.getInstance(myProject); + myVcsManager.registerVcs(myVcs); + myVcsManager.setDirectoryMapping("", myVcs.getName()); + + myLFS = LocalFileSystem.getInstance(); + myChangeListManager = ChangeListManager.getInstance(myProject); + ((ProjectComponent)myChangeListManager).projectOpened(); + myVcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); + ((ProjectComponent)myVcsDirtyScopeManager).projectOpened(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @After + public void tearDown() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + myVcsManager.unregisterVcs(myVcs); + ((ProjectComponent)myChangeListManager).projectClosed(); + ((ProjectComponent)myVcsDirtyScopeManager).projectClosed(); + + tearDownProject(); + if (myTempDirTestFixture != null) { + myTempDirTestFixture.tearDown(); + myTempDirTestFixture = null; + } + FileUtil.delete(myClientRoot); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @Test + public void testDeletion() throws Exception { + final File f = new File(myClientRoot, "f.txt"); + f.createNewFile(); + final VirtualFile vf = myLFS.refreshAndFindFileByIoFile(f); + myChangeListManager.ensureUpToDate(false); + ((ChangeListManagerImpl) myChangeListManager).getUnversionedFiles().contains(vf); + FileUtil.delete(f); + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + ((ChangeListManagerImpl) myChangeListManager).getUnversionedFiles().isEmpty(); + } + + @Test + public void testGeneration() throws Exception { + for (int i = 0; i < 100; i++) { + final File f = new File(myClientRoot, "f" + i + ".txt"); + f.createNewFile(); + } + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + final List unversionedFiles = ((ChangeListManagerImpl)myChangeListManager).getUnversionedFiles(); + final Pattern pattern = Pattern.compile("f([0-9])+\\.txt"); + int cnt = 0; + for (VirtualFile unversionedFile : unversionedFiles) { + if (VfsUtil.isAncestor(myWorkingCopyDir, unversionedFile, true)) { + ++ cnt; + Assert.assertTrue(pattern.matcher(unversionedFile.getName()).matches()); + } + } + Assert.assertEquals(100, cnt); + } + + /*@Test + public void testMoveDir() throws Exception { + final File root = new File(myClientRoot, "was"); + root.mkdir(); + for (int i = 0; i < 10; i++) { + final File dir = new File(root, "dir" + i); + dir.mkdir(); + for (int j = 0; j < 10; j++) { + final File f = new File(dir, "f" + j + ".txt"); + f.createNewFile(); + } + } + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + List unversionedFiles = ((ChangeListManagerImpl)myChangeListManager).getUnversionedFiles(); + final Pattern pattern = Pattern.compile("f([0-9])+\\.txt"); + final Pattern patternDir = Pattern.compile("dir([0-9])+"); + int cnt = 0; + for (VirtualFile unversionedFile : unversionedFiles) { + if (VfsUtil.isAncestor(myWorkingCopyDir, unversionedFile, true) && (! "was".equals(unversionedFile.getName()))) { + ++ cnt; + Assert.assertTrue(pattern.matcher(unversionedFile.getName()).matches() || patternDir.matcher(unversionedFile.getName()).matches()); + } + } + Assert.assertEquals(110, cnt); + final File renamedFile = new File(myClientRoot, "newName"); + FileUtil.copyDir(root, renamedFile); + FileUtil.delete(root); + + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + unversionedFiles = ((ChangeListManagerImpl)myChangeListManager).getUnversionedFiles(); + + cnt = 0; + for (VirtualFile unversionedFile : unversionedFiles) { + if (VfsUtil.isAncestor(myWorkingCopyDir, unversionedFile, true) && (! "newName".equals(unversionedFile.getName()))) { + ++ cnt; + Assert.assertTrue((pattern.matcher(unversionedFile.getName()).matches() || patternDir.matcher(unversionedFile.getName()).matches())); + if (unversionedFile.getPath().indexOf("newName") == -1) { + System.out.println("wrong " + unversionedFile.getPath()); + } + } + } + Assert.assertEquals(110, cnt); + }*/ + + private static class MyMockChangeProvider implements ChangeProvider { + @Override + public void doCleanup(List files) { + } + + @Override + public void getChanges(VcsDirtyScope dirtyScope, + final ChangelistBuilder builder, + ProgressIndicator progress, + ChangeListManagerGate addGate) throws VcsException { + for (FilePath path : dirtyScope.getDirtyFiles()) { + builder.processUnversionedFile(path.getVirtualFile()); + } + final Processor processor = new Processor() { + @Override + public boolean process(final VirtualFile vf) { + builder.processUnversionedFile(vf); + return true; + } + }; + for (FilePath dir : dirtyScope.getRecursivelyDirtyDirectories()) { + VfsUtil.processFilesRecursively(dir.getVirtualFile(), processor); + } + } + + @Override + public boolean isModifiedDocumentTrackingRequired() { + return false; + } + } +} diff --git a/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/VcsEventsListenerTest.java b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/VcsEventsListenerTest.java new file mode 100644 index 000000000000..f24ad1fed284 --- /dev/null +++ b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/VcsEventsListenerTest.java @@ -0,0 +1,355 @@ +package com.intellij.openapi.vcs.changes.committed; + +import com.intellij.ide.startup.impl.StartupManagerImpl; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeList; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.checkin.CheckinEnvironment; +import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; +import com.intellij.openapi.vcs.rollback.RollbackEnvironment; +import com.intellij.openapi.vcs.rollback.RollbackProgressListener; +import com.intellij.openapi.vcs.ui.RefreshableOnComponent; +import com.intellij.openapi.vcs.update.SequentialUpdatesContext; +import com.intellij.openapi.vcs.update.UpdateEnvironment; +import com.intellij.openapi.vcs.update.UpdateSession; +import com.intellij.openapi.vcs.update.UpdatedFiles; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TempDirTestFixture; +import com.intellij.testFramework.vcs.AbstractJunitVcsTestCase; +import com.intellij.util.NullableFunction; +import com.intellij.util.PairConsumer; +import com.intellij.util.ui.UIUtil; +import junit.framework.Assert; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * @author irengrig + * Date: 12/21/10 + * Time: 2:03 PM + */ +public class VcsEventsListenerTest extends AbstractJunitVcsTestCase { + private AbstractVcs myVcs; + private ProjectLevelVcsManagerImpl myVcsManager; + private ChangeListManager myChangeListManager; + private TempDirTestFixture myTempDirFixture; + private File myClientRoot; + + @Before + public void setUp() { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory(); + myTempDirFixture = fixtureFactory.createTempDirTestFixture(); + myTempDirFixture.setUp(); + + myClientRoot = new File(myTempDirFixture.getTempDirPath(), "clientroot"); + myClientRoot.mkdir(); + + initProject(myClientRoot, VcsEventsListenerTest.this.getTestName()); + + ((StartupManagerImpl)StartupManager.getInstance(myProject)).runPostStartupActivities(); + + myChangeListManager = ChangeListManager.getInstance(myProject); + myVcs = VcsActiveEnvironmentsProxy.proxyVcs(new MyVcs(myProject, "mock")); + myVcsManager = (ProjectLevelVcsManagerImpl)ProjectLevelVcsManager.getInstance(myProject); + myVcsManager.registerVcs(myVcs); + myVcsManager.setDirectoryMapping(myWorkingCopyDir.getPath(), myVcs.getName()); + + ((ProjectComponent)myChangeListManager).projectOpened(); + } + catch (Exception e) { + tearDown(); + throw new RuntimeException(e); + } + } + }); + } + + @After + public void tearDown() { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + if (myVcsManager != null && myVcs != null) { + myVcsManager.unregisterVcs(myVcs); + } + + ((ProjectComponent)myChangeListManager).projectClosed(); + tearDownProject(); + if (myTempDirFixture != null) { + myTempDirFixture.tearDown(); + myTempDirFixture = null; + } + FileUtil.delete(myClientRoot); + } + catch (Exception e) { + throw new RuntimeException(e); + } + finally { + try { + UsefulTestCase.clearFields(this); + } + catch (IllegalAccessException e) { + //noinspection ThrowFromFinallyBlock + throw new RuntimeException(e); + } + } + } + }); + } + + @Test + public void testSimpleListeningWithProxy() throws Exception { + final VcsEventsListenerManager manager = myVcsManager.getVcsEventsListenerManager(); + final List list = Arrays.asList(new VirtualFile[]{myWorkingCopyDir}); + final MyCheckinListener listener = new MyCheckinListener(list); + final Object key = manager.addCheckin(new ForwardingListener(listener)); + + myVcs.getCheckinEnvironment().scheduleUnversionedFilesForAddition(list); + + Assert.assertTrue(listener.isChecked()); + listener.assertCheckOk(); + + listener.reset(); + + manager.removeCheckin(key); + listener.reset(); + myVcs.getCheckinEnvironment().scheduleUnversionedFilesForAddition(list); + Assert.assertFalse(listener.isChecked()); + } + + @Test + public void testSimpleListeningWithoutProxy() throws Exception { + myVcsManager.setDirectoryMapping(myWorkingCopyDir.getPath(), "svn"); + + testSimpleListeningWithProxy(); + } + + private static class MyVcs extends MockAbstractVcs { + private MyVcs(Project project, String name) { + super(project, name); + } + + @Override + protected UpdateEnvironment createUpdateEnvironment() { + return new UpdateEnvironment() { + @Override + public void fillGroups(UpdatedFiles updatedFiles) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @NotNull + @Override + public UpdateSession updateDirectories(@NotNull FilePath[] contentRoots, + UpdatedFiles updatedFiles, + ProgressIndicator progressIndicator, + @NotNull Ref context) throws ProcessCanceledException { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Configurable createConfigurable(Collection files) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean validateOptions(Collection roots) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + }; + } + + @Override + protected RollbackEnvironment createRollbackEnvironment() { + return new RollbackEnvironment() { + @Override + public String getRollbackOperationName() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackChanges(List changes, + List vcsExceptions, + @NotNull RollbackProgressListener listener) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackMissingFileDeletion(List files, + List exceptions, + RollbackProgressListener listener) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackModifiedWithoutCheckout(List files, + List exceptions, + RollbackProgressListener listener) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackIfUnchanged(VirtualFile file) { + //To change body of implemented methods use File | Settings | File Templates. + } + }; + } + + @Override + protected CheckinEnvironment createCheckinEnvironment() { + return new CheckinEnvironment() { + @Override + public RefreshableOnComponent createAdditionalOptionsPanel(CheckinProjectPanel panel, + PairConsumer additionalDataConsumer) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getDefaultMessageFor(FilePath[] filesToCheckin) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getHelpId() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getCheckinOperationName() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List commit(List changes, String preparedComment) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List commit(List changes, + String preparedComment, + @NotNull NullableFunction parametersHolder, Set feedback) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List scheduleMissingFileForDeletion(List files) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List scheduleUnversionedFilesForAddition(List files) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean keepChangeListAfterCommit(ChangeList changeList) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean isRefreshAfterCommitNeeded() { + return true; + } + }; + } + } + + private static class MyCheckinListener implements CheckinEnvironment { + private boolean myChecked; + private boolean myAssertOk; + private final List myCheckList; + + public MyCheckinListener(final List checkList) { + myCheckList = checkList; + myChecked = false; + myAssertOk = true; + } + + @Override + public RefreshableOnComponent createAdditionalOptionsPanel(CheckinProjectPanel panel, + PairConsumer additionalDataConsumer) { + return null; + } + @Override + public String getDefaultMessageFor(FilePath[] filesToCheckin) { + return null; + } + @Override + public String getHelpId() { + return null; + } + @Override + public String getCheckinOperationName() { + return null; + } + @Override + public List commit(List changes, String preparedComment) { + return null; + } + + @Override + public List commit(List changes, + String preparedComment, + @NotNull NullableFunction parametersHolder, Set feedback) { + return null; + } + + @Override + public List scheduleMissingFileForDeletion(List files) { + return null; + } + + @Override + public List scheduleUnversionedFilesForAddition(List files) { + myChecked = true; + myAssertOk = myCheckList.equals(files); + return null; + } + + @Override + public boolean keepChangeListAfterCommit(ChangeList changeList) { + return false; + } + + @Override + public boolean isRefreshAfterCommitNeeded() { + return true; + } + + public boolean isChecked() { + return myChecked; + } + + public void assertCheckOk() { + Assert.assertTrue(myAssertOk); + } + + public void reset() { + myAssertOk = true; + myChecked = false; + } + } +} diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java similarity index 100% rename from platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java rename to platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java similarity index 100% rename from platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java rename to platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java index 476bab69af06..d76862ffb3d9 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java @@ -10,7 +10,10 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; -import com.intellij.vcs.log.*; +import com.intellij.vcs.log.Hash; +import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.VcsShortCommitDetails; import com.intellij.vcs.log.data.LoadingDetails; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.data.VisiblePack; @@ -157,6 +160,7 @@ public class GraphTableModel extends AbstractTableModel { /** * Requests the proper data provider to load more data from the log & recreate the model. + * * @param onLoaded will be called upon task completion on the EDT. */ public void requestToLoadMore(@NotNull Runnable onLoaded) { @@ -183,7 +187,9 @@ public class GraphTableModel extends AbstractTableModel { return ""; } else { - return data.getAuthor().getName() + (data.getAuthor().equals(data.getCommitter()) ? "" : "*"); + String authorString = data.getAuthor().getName(); + if (authorString.isEmpty()) authorString = data.getAuthor().getEmail(); + return authorString + (data.getAuthor().equals(data.getCommitter()) ? "" : "*"); } case DATE_COLUMN: if (data == null || data.getAuthorTime() < 0) { @@ -208,6 +214,7 @@ public class GraphTableModel extends AbstractTableModel { * Returns Changes for commits at selected rows.
* Rows are given in the order as they appear in the table, i. e. in reverse chronological order.
* Changes can be returned as-is, i.e. with duplicate changes for a single file. + * * @return Changes selected in all rows, or null if this data is not ready yet. */ @Nullable diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java index e83d1eb9f657..834f77d9e63e 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java @@ -302,10 +302,14 @@ public class XDebugSessionImpl implements XDebugSession { disableSlaveBreakpoints(dependentBreakpointManager); processAllBreakpoints(true, false); - myBreakpointListener = new MyBreakpointListener(); - breakpointManager.addBreakpointListener(myBreakpointListener); - myDependentBreakpointListener = new MyDependentBreakpointListener(); - dependentBreakpointManager.addListener(myDependentBreakpointListener); + if (myBreakpointListener == null) { + myBreakpointListener = new MyBreakpointListener(); + breakpointManager.addBreakpointListener(myBreakpointListener); + } + if (myDependentBreakpointListener == null) { + myDependentBreakpointListener = new MyDependentBreakpointListener(); + dependentBreakpointManager.addListener(myDependentBreakpointListener); + } } @Override diff --git a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java index 50ba95a046fb..68d5aba3f6e0 100644 --- a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java @@ -15,6 +15,7 @@ */ package git4idea.actions; +import com.intellij.dvcs.repo.Repository; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -114,19 +115,18 @@ public abstract class GitRepositoryAction extends DumbAwareAction { if (files != null) { for (VirtualFile file : files) { GitRepositoryManager manager = GitUtil.getRepositoryManager(project); - if (manager == null) { - return false; - } - final GitRepository repositoryForFile = manager.getRepositoryForFile(file); - if (repositoryForFile != null && repositoryForFile.getState() == GitRepository.State.REBASING) { - return true; - } + if (isRebasing(manager.getRepositoryForFile(file))) return true; } } + if (isRebasing(GitBranchUtil.getCurrentRepository(project))) return true; } return false; } + private static boolean isRebasing(@Nullable GitRepository repository) { + return repository != null && repository.getState() == Repository.State.REBASING; + } + /** * Get git roots for the project. The method shows dialogs in the case when roots cannot be retrieved, so it should be called * from the event dispatch thread. diff --git a/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java b/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java index cc2b8c0245a5..0091fdd085d6 100644 --- a/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java +++ b/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java @@ -15,6 +15,7 @@ */ package git4idea.repo; +import com.intellij.dvcs.DvcsUtil; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; @@ -298,7 +299,7 @@ public class GitUntrackedFilesHolder implements Disposable, BulkFileListener { private boolean belongsToThisRepository(VirtualFile file) { // this check should be quick // we shouldn't create a full instance repository here because it may lead to SOE while many unversioned files will be processed - final GitRepository repository = myRepositoryManager.getRepositoryForFile(file, false); + final GitRepository repository = myRepositoryManager.getRepositoryForRootQuick(DvcsUtil.getVcsRoot(myProject, file)); return repository != null && repository.getRoot().equals(myRoot); } } diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java index f0273b478608..4a270259d8c0 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java @@ -130,4 +130,23 @@ public class JUnitRerunFailedTestsTest extends LightCodeInsightFixtureTestCase }); assertNull(TestMethods.getTestPresentation(testProxy, project, searchScope)); } + + public void testInnerClass() throws Exception { + myFixture.addClass("public class TestClass {\n" + + " public static class Tests extends junit.framework.TestCase {\n" + + " public void testFoo() throws Exception {}\n" + + " }\n" + + "}"); + + final SMTestProxy testProxy = new SMTestProxy("testFoo", false, "java:test://TestClass$Tests.testFoo"); + final Project project = getProject(); + final GlobalSearchScope searchScope = GlobalSearchScope.projectScope(project); + testProxy.setLocator(JavaTestLocator.INSTANCE); + Location location = testProxy.getLocation(project, searchScope); + assertNotNull(location); + PsiElement element = location.getPsiElement(); + assertTrue(element instanceof PsiMethod); + String name = ((PsiMethod)element).getName(); + assertEquals(name, "testFoo"); + } } diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java index d46dff96d663..c5c051fe47bb 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java @@ -65,26 +65,25 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + - "##teamcity[testSuiteFinished name='root']\n" + - "##teamcity[testSuiteStarted name ='TestA']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='TestA']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + "##teamcity[testSuiteFinished name='|[1|]']\n" + "##teamcity[testSuiteFinished name='TestA']\n" + - "##teamcity[testSuiteStarted name ='TestB']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='TestB']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestB.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestB.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + @@ -109,17 +108,16 @@ public class JUnitTreeByDescriptionHierarchyTest { //start "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'TestA' comment = 'a' location = 'java:suite://a.TestA']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + - "##teamcity[testSuiteFinished name='|[1|]']\n" + - "##teamcity[testSuiteFinished name='TestA']\n"); + "##teamcity[testSuiteFinished name='|[1|]']\n"); } @Test @@ -154,16 +152,15 @@ public class JUnitTreeByDescriptionHierarchyTest { //started "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + - "##teamcity[testSuiteFinished name='root']\n" + - "##teamcity[testSuiteStarted name ='ASuite1']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + + "##teamcity[testSuiteStarted name='ASuite1']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + "##teamcity[testStarted name='test1' locationHint='java:test://ATest.test1']\n" + "\n" + "##teamcity[testFinished name='test1']\n" + "##teamcity[testSuiteFinished name='ATest']\n" + "##teamcity[testSuiteFinished name='ASuite1']\n" + - "##teamcity[testSuiteStarted name ='ASuite2']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + + "##teamcity[testSuiteStarted name='ASuite2']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + "##teamcity[testStarted name='test1' locationHint='java:test://ATest.test1']\n" + "\n" + "##teamcity[testFinished name='test1']\n" + @@ -204,8 +201,37 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[rootName name = 'TestA' location = 'java:suite://TestA']\n" + "##teamcity[testStarted name='testName' locationHint='java:test://TestA.testName']\n" + "\n" + - "##teamcity[testFinished name='testName']\n" + - "##teamcity[testSuiteFinished name='TestA']\n"); + "##teamcity[testFinished name='testName']\n"); + } + + @Test + public void testParameterizedTestsUpsideDown() throws Exception { + final Description aTestClass = Description.createSuiteDescription("ATest"); + final ArrayList tests = new ArrayList(); + final Description testMethod = Description.createSuiteDescription("testName"); + aTestClass.addChild(testMethod); + for (String paramName : new String[]{"[0]", "[1]"}) { + final Description testDescription = Description.createTestDescription("ATest", "testName" + paramName); + tests.add(testDescription); + testMethod.addChild(testDescription); + } + doTest(aTestClass, tests, + "##teamcity[suiteTreeStarted name='testName' locationHint='java:suite://testName']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + + "##teamcity[suiteTreeEnded name='testName']\n", + + + "##teamcity[enteredTheMatrix]\n" + + "##teamcity[rootName name = 'ATest' location = 'java:suite://ATest']\n" + + "##teamcity[testSuiteStarted name='testName']\n" + + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + + "\n" + + "##teamcity[testFinished name='testName|[0|]']\n" + + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + + "\n" + + "##teamcity[testFinished name='testName|[1|]']\n" + + "##teamcity[testSuiteFinished name='testName']\n"); } @Test @@ -240,21 +266,20 @@ public class JUnitTreeByDescriptionHierarchyTest { //start "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + - "##teamcity[testSuiteFinished name='root']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + "##teamcity[testSuiteFinished name='|[1|]']\n" + "##teamcity[testSuiteFinished name='ATest']\n" + - "##teamcity[testSuiteStarted name ='suite']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + + "##teamcity[testSuiteStarted name='suite']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + "##teamcity[testStarted name='test' locationHint='java:test://ATest.test']\n" + "\n" + "##teamcity[testFinished name='test']\n" + @@ -298,7 +323,6 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[rootName name = 'TestA' location = 'java:suite://TestA']\n" + "##teamcity[testStarted name='warning' locationHint='java:test://junit.framework.TestSuite$1.warning']\n" + "\n" + - "##teamcity[testFinished name='warning']\n" + - "##teamcity[testSuiteFinished name='TestA']\n"); + "##teamcity[testFinished name='warning']\n"); } } diff --git a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java index 453a60e6fe7c..b835081a92b3 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java +++ b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java @@ -32,6 +32,8 @@ import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunListener; import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.*; public class SMTestSender extends RunListener { @@ -41,16 +43,14 @@ public class SMTestSender extends RunListener { public static final String EMPTY_SUITE_NAME = "junit.framework.TestSuite$1"; public static final String EMPTY_SUITE_WARNING = "warning"; - private String myCurrentSuiteName; - private String myCurrentClassName; - private String myParamName; + private List myStartedSuites = new ArrayList(); + private Map myParents = new HashMap(); + private final PrintStream myPrintStream; + private String myRootName; - private PrintStream myPrintStream = System.out; - private final Map myParents = new HashMap(); - private final Map mySuites = new HashMap(); - - - public SMTestSender() {} + public SMTestSender() { + myPrintStream = System.out; + } public SMTestSender(PrintStream printStream) { myPrintStream = printStream; @@ -62,112 +62,90 @@ public class SMTestSender extends RunListener { public void testRunStarted(Description description) throws Exception { myPrintStream.println("##teamcity[enteredTheMatrix]"); - if (myCurrentClassName != null && !myCurrentClassName.startsWith("[")) { - int lastPointIdx = myCurrentClassName.lastIndexOf('.'); - String name = myCurrentClassName; + if (myRootName != null && !myRootName.startsWith("[")) { + int lastPointIdx = myRootName.lastIndexOf('.'); + String name = myRootName; String comment = null; if (lastPointIdx >= 0) { - name = myCurrentClassName.substring(lastPointIdx + 1); - comment = myCurrentClassName.substring(0, lastPointIdx); + name = myRootName.substring(lastPointIdx + 1); + comment = myRootName.substring(0, lastPointIdx); } myPrintStream.println("##teamcity[rootName name = \'" + escapeName(name) + (comment != null ? ("\' comment = \'" + escapeName(comment)) : "") + "\'" + - " location = \'java:suite://" + escapeName(myCurrentClassName) + + " location = \'java:suite://" + escapeName(myRootName) + "\']"); - myCurrentClassName = getShortName(myCurrentClassName); + myRootName = getShortName(myRootName); } } public void testRunFinished(Result result) throws Exception { - if (myParamName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myParamName) + "\']"); - } - if (myCurrentClassName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myCurrentClassName) + "\']"); - } - if (myCurrentSuiteName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']"); + for (int i = myStartedSuites.size() - 1; i>= 0; i--) { + Object parent = myStartedSuites.get(i); + myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName((String)parent) + "\']"); } + myStartedSuites.clear(); } public void testStarted(Description description) throws Exception { final String methodName = JUnit4ReflectionUtil.getMethodName(description); final String classFQN = JUnit4ReflectionUtil.getClassName(description); - final int paramStart = methodName.indexOf('['); - if (myParamName != null){ - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myParamName) + "\']"); - myParamName = null; - } - final List suites = (List)myParents.get(description); - if (suites != null && !suites.isEmpty()) { - String currentSuite = (String)suites.get(0); - List descriptors = (List)mySuites.get(currentSuite); + final List parents = (List)myParents.get(description); + if (parents != null) { - if (descriptors.isEmpty()) { - currentSuite = (String)suites.get(1); - descriptors = (List)mySuites.get(currentSuite); + List parentsHierarchy = (List)parents.remove(0); + + int idx = 0; + String currentClass; + String currentParent; + while (idx < myStartedSuites.size() && idx < parentsHierarchy.size()) { + currentClass = (String)myStartedSuites.get(idx); + currentParent = getShortName(JUnit4ReflectionUtil.getClassName((Description)parentsHierarchy.get(parentsHierarchy.size() - 1 - idx))); + if (!currentClass.equals(currentParent)) break; + idx++; } - if (!currentSuite.equals(myCurrentSuiteName)) { - finishCurrentSuite(); - myCurrentSuiteName = currentSuite; - myPrintStream.println("##teamcity[testSuiteStarted name =\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']"); + for (int i = myStartedSuites.size() - 1; i >= idx; i--) { + currentClass = (String)myStartedSuites.remove(i); + myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(currentClass) + "\']"); } - descriptors.remove(description); - } - else if (myCurrentSuiteName != null){ - finishCurrentSuite(); - myCurrentSuiteName = null; - } - - String className = getShortName(classFQN); - if (!myEmptyTests.isEmpty() && isWarning(methodName, classFQN)) { - className = (String)myEmptyTests.remove(0); - } - - if (!className.equals(myCurrentClassName)) { - if (myCurrentClassName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myCurrentClassName) + "\']"); - } - myCurrentClassName = className; - myPrintStream.println("##teamcity[testSuiteStarted name =\'" + escapeName(myCurrentClassName) + "\']"); - } - if (paramStart > -1) { - final String paramName = methodName.substring(paramStart, methodName.length()); - if (!paramName.equals(myParamName)) { - myParamName = paramName; - myPrintStream.println("##teamcity[testSuiteStarted name =\'" + escapeName(myParamName) + "\']"); + for (int i = idx; i < parentsHierarchy.size(); i++) { + Description parent = (Description)parentsHierarchy.get(parentsHierarchy.size() - 1 - i); + final String className = getShortName(JUnit4ReflectionUtil.getClassName(parent)); + if (!className.equals(myRootName)) { + myPrintStream.println("##teamcity[testSuiteStarted name=\'" + escapeName(className) + "\']"); + myStartedSuites.add(className); + } } } + myPrintStream.println("##teamcity[testStarted name=\'" + escapeName(methodName) + "\' " + getTestMethodLocation(methodName, classFQN) + "]"); } - private void finishCurrentSuite() { - if (myCurrentClassName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myCurrentClassName) + "\']"); - myCurrentClassName = null; - } - if (myCurrentSuiteName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']"); - } - } - public void testFinished(Description description) throws Exception { myPrintStream.println("\n##teamcity[testFinished name=\'" + escapeName(JUnit4ReflectionUtil.getMethodName(description)) + "\']"); } public void testFailure(Failure failure) throws Exception { - final String failureMessage = failure.getMessage(); - final String trace = failure.getTrace(); final Map attrs = new HashMap(); attrs.put("name", JUnit4ReflectionUtil.getMethodName(failure.getDescription())); - final ComparisonFailureData notification = createExceptionNotification(failure.getException()); - ComparisonFailureData.registerSMAttributes(notification, trace, failureMessage, attrs); - myPrintStream.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs)); + try { + final String trace = failure.getTrace(); + final ComparisonFailureData notification = createExceptionNotification(failure.getException()); + ComparisonFailureData.registerSMAttributes(notification, trace, failure.getMessage(), attrs); + } + catch (Throwable e) { + final StringWriter stringWriter = new StringWriter(); + final PrintWriter writer = new PrintWriter(stringWriter); + e.printStackTrace(writer); + ComparisonFailureData.registerSMAttributes(null, stringWriter.toString(), e.getMessage(), attrs); + } + finally { + myPrintStream.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs)); + } } public void testAssumptionFailure(Failure failure) { @@ -249,39 +227,29 @@ public class SMTestSender extends RunListener { return messageLength < threshold; } - private final List myEmptyTests = new ArrayList(); - - private void sendTree(Description description, Description parent, Description suiteParent) { + private void sendTree(Description description, Description parent, List currentParents) { + List pParents = new ArrayList(3); + pParents.addAll(currentParents); + if (parent != null && !myRootName.equals(JUnit4ReflectionUtil.getClassName(parent))) { + pParents.add(0, parent); + } + String className = JUnit4ReflectionUtil.getClassName(description); if (description.getChildren().isEmpty()) { final String methodName = JUnit4ReflectionUtil.getMethodName((Description)description); if (methodName != null) { - if (isWarning(methodName, className)) { - className = JUnit4ReflectionUtil.getClassName(parent); - myEmptyTests.add(getShortName(className)); - } - myPrintStream.println("##teamcity[suiteTreeNode name=\'" + escapeName(methodName) + "\' " + getTestMethodLocation(methodName, className) + "]"); - } - - if (suiteParent != null ) { - final String parentFQName = JUnit4ReflectionUtil.getClassName(suiteParent); - if (!myCurrentClassName.equals(parentFQName)) { + if (parent != null) { List parents = (List)myParents.get(description); if (parents == null) { - parents = new ArrayList(); + parents = new ArrayList(1); myParents.put(description, parents); } - if (!parents.contains(parentFQName)) { - parents.add(parentFQName); - } - - List descriptors = (List)mySuites.get(parentFQName); - if (descriptors == null) { - descriptors = new ArrayList(); - mySuites.put(parentFQName, descriptors); - } - descriptors.add(description); + parents.add(pParents); } + if (isWarning(methodName, className)) { + className = JUnit4ReflectionUtil.getClassName(parent); + } + myPrintStream.println("##teamcity[suiteTreeNode name=\'" + escapeName(methodName) + "\' " + getTestMethodLocation(methodName, className) + "]"); } return; @@ -292,8 +260,7 @@ public class SMTestSender extends RunListener { for (Iterator iterator = tests.iterator(); iterator.hasNext(); ) { final Object next = iterator.next(); final Description nextDescription = (Description)next; - final List childTests = nextDescription.getChildren(); - if ((myCurrentClassName == null || !myCurrentClassName.equals(className)) && !pass) { + if ((myRootName == null || !myRootName.equals(className)) && !pass) { pass = true; String locationHint = className; if (isParameter((Description)description)) { @@ -308,7 +275,7 @@ public class SMTestSender extends RunListener { } myPrintStream.println("##teamcity[suiteTreeStarted name=\'" + escapeName(getShortName(className)) + "\' locationHint=\'java:suite://" + escapeName(locationHint) + "\']"); } - sendTree(nextDescription, description, isParameter(description) ? null : childTests.isEmpty() && parent != null ? parent : description); + sendTree(nextDescription, description, pParents); } if (pass) { myPrintStream.println("##teamcity[suiteTreeEnded name=\'" + escapeName(getShortName(JUnit4ReflectionUtil.getClassName((Description)description))) + "\']"); @@ -329,8 +296,8 @@ public class SMTestSender extends RunListener { } public void sendTree(Description description) { - myCurrentClassName = JUnit4ReflectionUtil.getClassName((Description)description); - sendTree(description, null, null); + myRootName = JUnit4ReflectionUtil.getClassName((Description)description); + sendTree(description, null, new ArrayList()); } private static String getShortName(String fqName) { diff --git a/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java b/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java index 16a7b86261b3..b460e0f03de9 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java +++ b/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java @@ -24,16 +24,27 @@ import com.jetbrains.python.psi.PyStringLiteralExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.regex.Pattern; + /** * @author vlan */ public class PyTypingAnnotationInjector extends PyInjectorBase { + public static final Pattern RE_TYPING_ANNOTATION = Pattern.compile("\\s*\\S+(\\[.*\\])?\\s*"); + @Nullable @Override public Language getInjectedLanguage(@NotNull PsiElement context) { - if (context instanceof PyStringLiteralExpression && PsiTreeUtil.getParentOfType(context, PyAnnotation.class, true) != null) { - return PyDocstringLanguageDialect.getInstance(); + if (context instanceof PyStringLiteralExpression) { + final PyStringLiteralExpression expr = (PyStringLiteralExpression)context; + if (PsiTreeUtil.getParentOfType(context, PyAnnotation.class, true) != null && isTypingAnnotation(expr.getStringValue())) { + return PyDocstringLanguageDialect.getInstance(); + } } return null; } + + private static boolean isTypingAnnotation(@NotNull String s) { + return RE_TYPING_ANNOTATION.matcher(s).matches(); + } } diff --git a/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java b/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java index ae7229d56b8f..e00010e08903 100644 --- a/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java +++ b/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java @@ -171,7 +171,7 @@ public class PyContentEntriesEditor extends CommonContentEntriesEditor { } VirtualFilePointer root = getRoot(provider, folder); if (root != null) { - provider.removeRoot(contentEntry, root); + provider.removeRoot(contentEntry, root, getModel()); fireUpdate(); } } diff --git a/python/src/com/jetbrains/python/module/PyRootTypeProvider.java b/python/src/com/jetbrains/python/module/PyRootTypeProvider.java index 56610ea41ae3..dd5a0631876e 100644 --- a/python/src/com/jetbrains/python/module/PyRootTypeProvider.java +++ b/python/src/com/jetbrains/python/module/PyRootTypeProvider.java @@ -56,7 +56,7 @@ public abstract class PyRootTypeProvider { public abstract boolean isMine(ContentFolder folder); - public void removeRoot(ContentEntry contentEntry, @NotNull final VirtualFilePointer root) { + public void removeRoot(ContentEntry contentEntry, @NotNull final VirtualFilePointer root, ModifiableRootModel model) { getRoots().remove(contentEntry, root); } public abstract MultiMap getRoots(); diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 4d104f1c2629..5ec8c8d0e16b 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -23,6 +23,7 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.scratch.ScratchFileService; import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.lang.ASTFactory; import com.intellij.lang.ASTNode; @@ -1803,6 +1804,16 @@ public class PyUtil { return type == builtinCache.getObjectType() || type == builtinCache.getOldstyleClassobjType(); } + public static boolean isInScratchFile(@NotNull PsiElement element) { + final ScratchFileService service = ScratchFileService.getInstance(); + final PsiFile file = element.getContainingFile(); + if (file != null) { + final VirtualFile virtualFile = file.getVirtualFile(); + return service != null && virtualFile != null && service.getRootType(virtualFile) != null; + } + return false; + } + /** * This helper class allows to collect various information about AST nodes composing {@link PyStringLiteralExpression}. */ diff --git a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java index b073db8c6955..e0ce6f2dfc1a 100644 --- a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java +++ b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java @@ -37,6 +37,7 @@ import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil; import com.jetbrains.python.console.PydevConsoleRunner; import com.jetbrains.python.facet.PythonPathContributingFacet; import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyImportResolver; import com.jetbrains.python.sdk.PySdkUtil; @@ -84,7 +85,7 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso @Override public QualifiedNameResolver fromElement(@NotNull PsiElement foothold) { myContext.setFromElement(foothold); - if (PydevConsoleRunner.isInPydevConsole(foothold)) { + if (PydevConsoleRunner.isInPydevConsole(foothold) || PyUtil.isInScratchFile(foothold)) { withAllModules(); Sdk sdk = PydevConsoleRunner.getConsoleSdk(foothold); if (sdk != null) { diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index 6ac1fdfd5c2e..b6126e9abeb6 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -15,6 +15,15 @@ */ package com.jetbrains.python; +import com.intellij.lang.injection.InjectedLanguageManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.testFramework.LightProjectDescriptor; import com.jetbrains.python.documentation.PythonDocumentationProvider; import com.jetbrains.python.fixtures.PyTestCase; @@ -25,6 +34,8 @@ import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + /** * Tests for a type system based on mypy's typing module. * @@ -306,6 +317,59 @@ public class PyTypingTest extends PyTestCase { " pass\n"); } + public void testStringLiteralInjection() { + doTestInjectedText("class C:\n" + + " def foo(self, expr: 'C'):\n" + + " pass\n", + "C"); + } + + public void testStringLiteralInjectionParameterizedType() { + doTestInjectedText("from typing import Union, List\n" + + "\n" + + "class C:\n" + + " def foo(self, expr: 'Union[List[C], C]'):\n" + + " pass\n", + "Union[List[C], C]"); + } + + // PY-15810 + public void testNoStringLiteralInjectionForNonTypingStrings() { + doTestNoInjectedText("class C:\n" + + " def foo(self, expr: 'foo bar'):\n" + + " pass\n"); + } + + private void doTestNoInjectedText(@NotNull String text) { + myFixture.configureByText(PythonFileType.INSTANCE, text); + final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject()); + final PsiLanguageInjectionHost host = languageManager.getInjectionHost(getElementAtCaret()); + assertNull(host); + } + + private void doTestInjectedText(@NotNull String text, @NotNull String expected) { + myFixture.configureByText(PythonFileType.INSTANCE, text); + final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject()); + final PsiLanguageInjectionHost host = languageManager.getInjectionHost(getElementAtCaret()); + assertNotNull(host); + final List> files = languageManager.getInjectedPsiFiles(host); + assertNotNull(files); + assertFalse(files.isEmpty()); + final PsiElement injected = files.get(0).getFirst(); + assertEquals(expected, injected.getText()); + } + + @NotNull + private PsiElement getElementAtCaret() { + final Editor editor = myFixture.getEditor(); + final Document document = editor.getDocument(); + final PsiFile file = PsiDocumentManager.getInstance(myFixture.getProject()).getPsiFile(document); + assertNotNull(file); + final PsiElement element = file.findElementAt(myFixture.getCaretOffset()); + assertNotNull(element); + return element; + } + private void doTest(@NotNull String expectedType, @NotNull String text) { myFixture.copyDirectoryToProject("typing", ""); myFixture.configureByText(PythonFileType.INSTANCE, text); diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index fb9ce9850db6..2a72949c0945 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -448,6 +448,9 @@ + +