Merge remote-tracking branch 'origin/master'

This commit is contained in:
Roman Shevchenko
2015-07-02 15:53:14 +03:00
163 changed files with 3245 additions and 2344 deletions
+2 -2
View File
@@ -61,7 +61,7 @@ IDE_BIN_HOME=`dirname "$SCRIPT_LOCATION"`
# ---------------------------------------------------------------------
if [ -n "$@@product_uc@@_JDK" -a -x "$@@product_uc@@_JDK/bin/java" ]; then
JDK="$@@product_uc@@_JDK"
elif [ -x "$IDE_HOME/jre/bin/java" ] && "$IDE_HOME/jre/bin/java" -version > /dev/null 2>&1 ; then
elif [ -x "$IDE_HOME/jre/jre/bin/java" ] && "$IDE_HOME/jre/jre/bin/java" -version > /dev/null 2>&1 ; then
JDK="$IDE_HOME/jre"
elif [ -n "$JDK_HOME" -a -x "$JDK_HOME/bin/java" ]; then
JDK="$JDK_HOME"
@@ -173,7 +173,7 @@ fi
# ---------------------------------------------------------------------
# Run the IDE.
# ---------------------------------------------------------------------
LD_LIBRARY_PATH="$IDE_BIN_HOME:$LD_LIBRARY_PATH" "$JDK/bin/java" \
LD_LIBRARY_PATH="$IDE_BIN_HOME:$LD_LIBRARY_PATH" "$JDK/jre/bin/java" \
$AGENT \
"-Xbootclasspath/a:$IDE_HOME/lib/boot.jar" \
-classpath "$CLASSPATH" \
+1 -1
View File
@@ -172,7 +172,7 @@ def layoutAll(Map args, String home, String out, Paths _paths = null, buildJps =
buildTarGz("idea-${args.buildNumber}", "$paths.artifacts/idea${args.buildNumber}.tar", [paths.distAll, paths.distUnix])
if (p("jdk.bundled.linux") != "false") {
buildTarGz("idea-${args.buildNumber}", "$paths.artifacts/idea${args.buildNumber}-jdk-bundled.tar", [paths.distAll, paths.distUnix, "${paths.sandbox}/bundled.linux.jdk"], ["jre/bin/*"])
buildTarGz("idea-${args.buildNumber}", "$paths.artifacts/idea${args.buildNumber}-jdk-bundled.tar", [paths.distAll, paths.distUnix, "${paths.sandbox}/bundled.linux.jdk"], ["jre/jre/bin/*"])
}
return info
}
-1
View File
@@ -260,7 +260,6 @@ libraryLicense(name: "protobuf", version: "2.5.0", license: "New BSD", url: "htt
libraryLicense(name: "Netty", libraryName: "Netty", version: "4.1.0.Beta3", license: "Apache 2.0", url: "http://netty.io", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0")
libraryLicense(name: "Kryo", libraryName: "Kryo", version: "2.22", license: "New BSD License", url: "https://github.com/EsotericSoftware/kryo", licenseUrl: "https://github.com/EsotericSoftware/kryo/blob/master/license.txt")
libraryLicense(name: "Snappy-Java", libraryName: "Snappy-Java", version: "0.3.1", license: "Apache 2.0", url: "https://github.com/dain/snappy", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0")
libraryLicense(name: "Cucumber-Java", libraryName: "cucumber-java", version: "1.2.2", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html")
libraryLicense(name: "Cucumber-JVM", libraryName: "cucumber-jvm", version: "1.2.2", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html")
libraryLicense(name: "Cucumber-Groovy", libraryName: "cucumber-groovy", version: "1.2.2", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html")
libraryLicense(name: "XStream", libraryName: "XStream", version: "1.4.2", license: "BSD License", url: "https://github.com/cucumber/cucumber-jvm-deps/", licenseUrl: "http://xstream.codehaus.org/license.html")
@@ -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.
@@ -26,6 +26,7 @@ import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.FindSuperElementsHelper;
import com.intellij.psi.impl.source.PsiClassImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiSearchHelper;
@@ -135,14 +136,15 @@ public class UnusedSymbolUtil {
}
else {
//class maybe used in some weird way, e.g. from XML, therefore the only constructor is used too
if (containingClass != null && method.isConstructor()
boolean isConstructor = method.isConstructor();
if (containingClass != null && isConstructor
&& containingClass.getConstructors().length == 1
&& isClassUsed(project, containingFile, containingClass, progress, helper)) {
return true;
}
if (isImplicitUsage(project, method, progress)) return true;
if (method.findSuperMethods().length != 0) {
if (!isConstructor && FindSuperElementsHelper.findSuperElements(method).length != 0) {
return true;
}
if (!weAreSureThereAreNoUsages(project, containingFile, method, progress, helper)) {
@@ -193,7 +195,7 @@ public class UnusedSymbolUtil {
@NotNull PsiFile containingFile,
@NotNull PsiMember member,
@NotNull ProgressIndicator progress,
final PsiFile ignoreFile,
@Nullable PsiFile ignoreFile,
@NotNull Processor<UsageInfo> usageInfoProcessor) {
String name = member.getName();
if (name == null) {
@@ -247,10 +249,8 @@ public class UnusedSymbolUtil {
}
else if (member instanceof PsiMethod) {
PsiMethod method = (PsiMethod)member;
JavaMethodFindUsagesOptions o = new JavaMethodFindUsagesOptions(project);
//o.isIncludeOverloadUsages = true;
options = o;
options.isSearchForTextOccurrences = method.isConstructor();;
options = new JavaMethodFindUsagesOptions(project);
options.isSearchForTextOccurrences = method.isConstructor();
}
else if (member instanceof PsiVariable) {
options = new JavaVariableFindUsagesOptions(project);
@@ -271,7 +271,7 @@ public class UnusedSymbolUtil {
@NotNull ProgressIndicator progress,
@NotNull GlobalUsageHelper helper) {
final PsiClass containingClass = member.getContainingClass();
if (containingClass == null || !(containingClass instanceof PsiClassImpl)) return true;
if (!(containingClass instanceof PsiClassImpl)) return true;
final PsiMethod valuesMethod = ((PsiClassImpl)containingClass).getValuesMethod();
return valuesMethod == null || isMethodReferenced(project, containingFile, valuesMethod, progress, helper);
}
@@ -0,0 +1,125 @@
/*
* 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.searches.ClassInheritorsSearch;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiSuperMethodUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.FactoryMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class FindSuperElementsHelper {
@NotNull
public static PsiElement[] findSuperElements(@NotNull PsiElement element) {
if (element instanceof PsiClass) {
PsiClass aClass = (PsiClass) element;
List<PsiClass> allSupers = new ArrayList<PsiClass>(Arrays.asList(aClass.getSupers()));
for (Iterator<PsiClass> iterator = allSupers.iterator(); iterator.hasNext();) {
PsiClass superClass = iterator.next();
if (CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) iterator.remove();
}
return allSupers.toArray(new PsiClass[allSupers.size()]);
}
if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod) element;
if (method.isConstructor()) {
PsiMethod constructorInSuper = PsiSuperMethodUtil.findConstructorInSuper(method);
if (constructorInSuper != null) {
return new PsiMethod[]{constructorInSuper};
}
}
else {
PsiMethod[] superMethods = method.findSuperMethods(false);
if (superMethods.length == 0) {
PsiMethod superMethod = getSiblingInheritedViaSubClass(method);
if (superMethod != null) {
superMethods = new PsiMethod[]{superMethod};
}
}
return superMethods;
}
}
return PsiElement.EMPTY_ARRAY;
}
public static PsiMethod getSiblingInheritedViaSubClass(@NotNull PsiMethod method) {
return getSiblingInheritedViaSubClass(method, createSubClassCache());
}
public static PsiMethod getSiblingInheritedViaSubClass(@NotNull final PsiMethod method,
@NotNull Map<PsiClass, PsiClass> subClassCache) {
if (!method.hasModifierProperty(PsiModifier.PUBLIC)) return null;
if (method.hasModifierProperty(PsiModifier.STATIC)) return null;
final PsiClass containingClass = method.getContainingClass();
boolean hasSubClass = containingClass != null && !containingClass.isInterface() && subClassCache.get(containingClass) != null;
if (!hasSubClass) {
return null;
}
final Collection<PsiClass> checkedInterfaces = new THashSet<PsiClass>();
final PsiMethod[] result = new PsiMethod[1];
ClassInheritorsSearch.search(containingClass, true).forEach(new Processor<PsiClass>() {
@Override
public boolean process(PsiClass inheritor) {
for (PsiClassType interfaceType : inheritor.getImplementsListTypes()) {
PsiClassType.ClassResolveResult resolved = interfaceType.resolveGenerics();
PsiClass anInterface = resolved.getElement();
if (anInterface == null || !checkedInterfaces.add(anInterface)) continue;
for (PsiMethod superMethod : anInterface.findMethodsByName(method.getName(), true)) {
PsiClass superInterface = superMethod.getContainingClass();
if (superInterface == null) {
continue;
}
// calculate substitutor of containingClass --> inheritor
PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(containingClass, inheritor, PsiSubstitutor.EMPTY);
// calculate substitutor of inheritor --> superInterface
substitutor = TypeConversionUtil.getSuperClassSubstitutor(superInterface, inheritor, substitutor);
final MethodSignature superSignature = superMethod.getSignature(substitutor);
final MethodSignature derivedSignature = method.getSignature(PsiSubstitutor.EMPTY);
boolean isOverridden = MethodSignatureUtil.isSubsignature(superSignature, derivedSignature);
if (isOverridden) {
result[0] = superMethod;
return false;
}
}
}
return true;
}
});
return result[0];
}
@NotNull
public static Map<PsiClass, PsiClass> createSubClassCache() {
return new FactoryMap<PsiClass, PsiClass>() {
@Nullable
@Override
protected PsiClass create(PsiClass aClass) {
return ClassInheritorsSearch.search(aClass, false).findFirst();
}
};
}
}
@@ -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.
@@ -31,8 +31,8 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.FindSuperElementsHelper;
import com.intellij.psi.search.searches.AllOverridingMethodsSearch;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
import com.intellij.psi.search.searches.SuperMethodsSearch;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
@@ -48,12 +48,12 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JavaLineMarkerProvider implements LineMarkerProvider {
protected final DaemonCodeAnalyzerSettings myDaemonSettings;
protected final EditorColorsManager myColorsManager;
private final DaemonCodeAnalyzerSettings myDaemonSettings;
private final EditorColorsManager myColorsManager;
public JavaLineMarkerProvider(DaemonCodeAnalyzerSettings daemonSettings, EditorColorsManager colorsManager) {
myDaemonSettings = daemonSettings;
@@ -72,19 +72,14 @@ public class JavaLineMarkerProvider implements LineMarkerProvider {
method.hasModifierProperty(PsiModifier.ABSTRACT) == superSignature.getMethod().hasModifierProperty(PsiModifier.ABSTRACT);
final Icon icon = overrides ? AllIcons.Gutter.OverridingMethod : AllIcons.Gutter.ImplementingMethod;
final MarkerType type = MarkerType.OVERRIDING_METHOD;
ArrowUpLineMarkerInfo info = new ArrowUpLineMarkerInfo(element, icon, type);
return NavigateAction.setNavigateAction(info, "Go to super method", "GotoSuperMethod");
return createSuperMethodLineMarkerInfo(element, icon, Pass.UPDATE_ALL);
}
}
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(element);
final PsiElement firstChild = element.getFirstChild();
if (interfaceMethod != null && firstChild != null) {
final Icon icon = AllIcons.Gutter.ImplementingMethod;
final MarkerType type = MarkerType.OVERRIDING_METHOD;
ArrowUpLineMarkerInfo info = new ArrowUpLineMarkerInfo(firstChild, icon, type);
return NavigateAction.setNavigateAction(info, "Go to super method", "GotoSuperMethod");
return createSuperMethodLineMarkerInfo(firstChild, AllIcons.Gutter.ImplementingMethod, Pass.UPDATE_ALL);
}
if (myDaemonSettings.SHOW_METHOD_SEPARATORS && firstChild == null) {
@@ -128,6 +123,12 @@ public class JavaLineMarkerProvider implements LineMarkerProvider {
return null;
}
@NotNull
private static LineMarkerInfo createSuperMethodLineMarkerInfo(@NotNull PsiElement name, @NotNull Icon icon, int passId) {
ArrowUpLineMarkerInfo info = new ArrowUpLineMarkerInfo(name, icon, MarkerType.OVERRIDING_METHOD, passId);
return NavigateAction.setNavigateAction(info, "Go to super method", IdeActions.ACTION_GOTO_SUPER);
}
private static int getCategory(@NotNull PsiElement element, @NotNull CharSequence documentChars) {
if (element instanceof PsiField || element instanceof PsiTypeParameter) return 1;
if (element instanceof PsiClass || element instanceof PsiClassInitializer) return 2;
@@ -147,37 +148,87 @@ public class JavaLineMarkerProvider implements LineMarkerProvider {
@Override
public void collectSlowLineMarkers(@NotNull final List<PsiElement> elements, @NotNull final Collection<LineMarkerInfo> result) {
ApplicationManager.getApplication().assertReadAccessAllowed();
Map<PsiClass, PsiClass> subClassCache = FindSuperElementsHelper.createSubClassCache();
Set<PsiMethod> methods = new HashSet<PsiMethod>();
Collection<PsiMethod> methods = new THashSet<PsiMethod>();
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < elements.size(); i++) {
PsiElement element = elements.get(i);
ProgressManager.checkCanceled();
if (element instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)element;
if (!(element instanceof PsiIdentifier)) continue;
PsiElement parent = element.getParent();
if (parent instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)parent;
if (PsiUtil.canBeOverriden(method)) {
methods.add(method);
}
}
else if (element instanceof PsiClass && !(element instanceof PsiTypeParameter)) {
collectInheritingClasses((PsiClass)element, result);
else if (parent instanceof PsiClass && !(parent instanceof PsiTypeParameter)) {
collectInheritingClasses((PsiClass)parent, result, subClassCache);
}
}
if (!methods.isEmpty()) {
collectOverridingAccessors(methods, result);
collectOverridingMethods(methods, result);
collectSiblingInheritedMethods(methods, result, subClassCache);
}
}
public static void collectInheritingClasses(PsiClass aClass, Collection<LineMarkerInfo> result) {
private static void collectSiblingInheritedMethods(@NotNull final Collection<PsiMethod> methods,
@NotNull Collection<LineMarkerInfo> result,
@NotNull Map<PsiClass, PsiClass> subClassCache) {
for (PsiMethod method : methods) {
ProgressManager.checkCanceled();
PsiClass aClass = method.getContainingClass();
if (aClass == null || aClass.hasModifierProperty(PsiModifier.FINAL) || aClass.isInterface()) continue;
boolean canHaveSiblingSuper = !method.hasModifierProperty(PsiModifier.ABSTRACT) && !method.hasModifierProperty(PsiModifier.STATIC) && method.hasModifierProperty(PsiModifier.PUBLIC)&& !method.hasModifierProperty(PsiModifier.FINAL)&& !method.hasModifierProperty(PsiModifier.NATIVE);
if (!canHaveSiblingSuper) continue;
PsiMethod siblingInheritedViaSubClass = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method, subClassCache);
if (siblingInheritedViaSubClass == null) {
continue;
}
PsiElement range = getMethodRange(method);
LineMarkerInfo info = createSuperMethodLineMarkerInfo(range, AllIcons.Gutter.ImplementingMethod, Pass.UPDATE_OVERRIDEN_MARKERS);
result.add(info);
PsiClass sClass = siblingInheritedViaSubClass.getContainingClass();
String sName = sClass == null ? null : sClass.getQualifiedName();
}
}
@NotNull
private static PsiElement getMethodRange(@NotNull PsiMethod method) {
PsiElement range;
if (method.isPhysical()) {
range = method.getNameIdentifier();
}
else {
final PsiElement navigationElement = method.getNavigationElement();
range = navigationElement instanceof PsiNameIdentifierOwner
? ((PsiNameIdentifierOwner)navigationElement).getNameIdentifier()
: navigationElement;
}
if (range == null) {
range = method;
}
return range;
}
public static void collectInheritingClasses(@NotNull PsiClass aClass,
@NotNull Collection<LineMarkerInfo> result,
@NotNull Map<PsiClass, PsiClass> subClassCache) {
if (aClass.hasModifierProperty(PsiModifier.FINAL)) {
return;
}
if (CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) return; // It's useless to have overridden markers for object.
if (ClassInheritorsSearch.search(aClass, false).findFirst() != null || FunctionalExpressionSearch.search(aClass).findFirst() != null) {
PsiClass subClass = subClassCache.get(aClass);
if (subClass != null || FunctionalExpressionSearch.search(aClass).findFirst() != null) {
final Icon icon = aClass.isInterface() ? AllIcons.Gutter.ImplementedMethod : AllIcons.Gutter.OverridenMethod;
PsiElement range = aClass.getNameIdentifier();
if (range == null) range = aClass;
if (range == null) {
range = aClass;
}
MarkerType type = MarkerType.SUBCLASSED_CLASS;
LineMarkerInfo info = new LineMarkerInfo<PsiElement>(range, range.getTextRange(),
icon, Pass.UPDATE_OVERRIDEN_MARKERS, type.getTooltip(),
@@ -188,7 +239,7 @@ public class JavaLineMarkerProvider implements LineMarkerProvider {
}
}
private static void collectOverridingAccessors(final Set<PsiMethod> methods, Collection<LineMarkerInfo> result) {
private static void collectOverridingMethods(@NotNull final Collection<PsiMethod> methods, @NotNull Collection<LineMarkerInfo> result) {
final Set<PsiMethod> overridden = new HashSet<PsiMethod>();
Set<PsiClass> classes = new THashSet<PsiClass>();
for (PsiMethod method : methods) {
@@ -229,22 +280,9 @@ public class JavaLineMarkerProvider implements LineMarkerProvider {
ProgressManager.checkCanceled();
boolean overrides = !method.hasModifierProperty(PsiModifier.ABSTRACT);
final Icon icon = overrides ? AllIcons.Gutter.OverridenMethod : AllIcons.Gutter.ImplementedMethod;
PsiElement range;
if (method.isPhysical()) {
range = method.getNameIdentifier();
}
else {
final PsiElement navigationElement = method.getNavigationElement();
if (navigationElement instanceof PsiNameIdentifierOwner) {
range = ((PsiNameIdentifierOwner)navigationElement).getNameIdentifier();
}
else {
range = navigationElement;
}
}
if (range == null) range = method;
PsiElement range = getMethodRange(method);
final MarkerType type = MarkerType.OVERRIDDEN_METHOD;
final Icon icon = overrides ? AllIcons.Gutter.OverridenMethod : AllIcons.Gutter.ImplementedMethod;
LineMarkerInfo<PsiElement> info = new LineMarkerInfo<PsiElement>(range, range.getTextRange(),
icon, Pass.UPDATE_OVERRIDEN_MARKERS, type.getTooltip(),
type.getNavigationHandler(),
@@ -255,8 +293,8 @@ public class JavaLineMarkerProvider implements LineMarkerProvider {
}
private static class ArrowUpLineMarkerInfo extends MergeableLineMarkerInfo<PsiElement> {
private ArrowUpLineMarkerInfo(@NotNull PsiElement element, Icon icon, @NotNull MarkerType markerType) {
super(element, element.getTextRange(), icon, Pass.UPDATE_ALL, markerType.getTooltip(),
private ArrowUpLineMarkerInfo(@NotNull PsiElement element, @NotNull Icon icon, @NotNull MarkerType markerType, int passId) {
super(element, element.getTextRange(), icon, passId, markerType.getTooltip(),
markerType.getNavigationHandler(), GutterIconRenderer.Alignment.LEFT);
}
@@ -274,6 +312,7 @@ public class JavaLineMarkerProvider implements LineMarkerProvider {
return myIcon;
}
@NotNull
@Override
public Function<? super PsiElement, String> getCommonTooltip(@NotNull List<MergeableLineMarkerInfo> infos) {
return new Function<PsiElement, String>() {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 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.
@@ -34,6 +34,7 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.impl.FindSuperElementsHelper;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.search.PsiElementProcessorAdapter;
import com.intellij.psi.search.SearchScope;
@@ -45,6 +46,7 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Function;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -105,9 +107,9 @@ public class MarkerType {
});
@Nullable
public static String calculateOverridingMethodTooltip(PsiMethod method, boolean acceptSelf) {
private static String calculateOverridingMethodTooltip(@NotNull PsiMethod method, boolean acceptSelf) {
PsiMethod[] superMethods = composeSuperMethods(method, acceptSelf);
if (superMethods == null) return null;
if (superMethods.length == 0) return null;
PsiMethod superMethod = superMethods[0];
boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
@@ -121,10 +123,11 @@ public class MarkerType {
else{
key = sameSignature ? "method.overrides" : "method.overrides.in";
}
return composeText(superMethods, "", DaemonBundle.message(key), "GotoSuperMethod");
return composeText(superMethods, "", DaemonBundle.message(key), IdeActions.ACTION_GOTO_SUPER);
}
private static String composeText(PsiElement[] methods, String start, String pattern, String actionId) {
@NotNull
private static String composeText(@NotNull PsiElement[] methods, @NotNull String start, @NotNull String pattern, @NotNull String actionId) {
Shortcut[] shortcuts = ActionManager.getInstance().getAction(actionId).getShortcutSet().getShortcuts();
Shortcut shortcut = ArrayUtil.getFirstElement(shortcuts);
String postfix = "<br><div style='margin-top: 5px'><font size='2'>Click";
@@ -133,9 +136,9 @@ public class MarkerType {
return GutterIconTooltipHelper.composeText(Arrays.asList(methods), start, pattern, postfix);
}
public static void navigateToOverridingMethod(MouseEvent e, PsiMethod method, boolean acceptSelf) {
private static void navigateToOverridingMethod(MouseEvent e, @NotNull PsiMethod method, boolean acceptSelf) {
PsiMethod[] superMethods = composeSuperMethods(method, acceptSelf);
if (superMethods == null) return;
if (superMethods.length == 0) return;
boolean showMethodNames = !PsiUtil.allMethodsHaveSameSignature(superMethods);
PsiElementListNavigator.openTargets(e, superMethods,
DaemonBundle.message("navigation.title.super.method", method.getName()),
@@ -143,17 +146,23 @@ public class MarkerType {
new MethodCellRenderer(showMethodNames));
}
@Nullable
private static PsiMethod[] composeSuperMethods(PsiMethod method, boolean acceptSelf) {
PsiMethod[] superMethods = method.findSuperMethods(false);
@NotNull
private static PsiMethod[] composeSuperMethods(@NotNull PsiMethod method, boolean acceptSelf) {
PsiElement[] superElements = FindSuperElementsHelper.findSuperElements(method);
PsiMethod[] superMethods = ContainerUtil.map(superElements, new Function<PsiElement, PsiMethod>() {
@Override
public PsiMethod fun(PsiElement element) {
return (PsiMethod)element;
}
}, PsiMethod.EMPTY_ARRAY);
if (acceptSelf) {
superMethods = ArrayUtil.prepend(method, superMethods);
}
if (superMethods.length == 0) return null;
return superMethods;
}
private static PsiElement getParentMethod(PsiElement element) {
private static PsiElement getParentMethod(@NotNull PsiElement element) {
final PsiElement parent = element.getParent();
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(parent);
return interfaceMethod != null ? interfaceMethod : parent;
@@ -179,7 +188,7 @@ public class MarkerType {
}
});
public static String getOverriddenMethodTooltip(final PsiMethod method) {
private static String getOverriddenMethodTooltip(@NotNull PsiMethod method) {
PsiElementProcessor.CollectElementsWithLimit<PsiMethod> processor = new PsiElementProcessor.CollectElementsWithLimit<PsiMethod>(5);
OverridingMethodsSearch.search(method, true).forEach(new PsiElementProcessorAdapter<PsiMethod>(processor));
@@ -206,7 +215,7 @@ public class MarkerType {
return composeText(overridings, start, pattern, IdeActions.ACTION_GOTO_IMPLEMENTATION);
}
public static void navigateToOverriddenMethod(MouseEvent e, final PsiMethod method) {
private static void navigateToOverriddenMethod(MouseEvent e, @NotNull final PsiMethod method) {
if (DumbService.isDumb(method.getProject())) {
DumbService.getInstance(method.getProject()).showDumbModeNotification(
"Navigation to overriding classes is not possible during index update");
@@ -267,7 +276,7 @@ public class MarkerType {
}
});
public static String getSubclassedClassTooltip(PsiClass aClass) {
private static String getSubclassedClassTooltip(@NotNull PsiClass aClass) {
PsiElementProcessor.CollectElementsWithLimit<PsiClass> processor = new PsiElementProcessor.CollectElementsWithLimit<PsiClass>(5, new THashSet<PsiClass>());
ClassInheritorsSearch.search(aClass, true).forEach(new PsiElementProcessorAdapter<PsiClass>(processor));
@@ -298,7 +307,7 @@ public class MarkerType {
return composeText(subclasses, start, pattern, IdeActions.ACTION_GOTO_IMPLEMENTATION);
}
public static void navigateToSubclassedClass(MouseEvent e, final PsiClass aClass) {
private static void navigateToSubclassedClass(MouseEvent e, @NotNull final PsiClass aClass) {
if (DumbService.isDumb(aClass.getProject())) {
DumbService.getInstance(aClass.getProject()).showDumbModeNotification("Navigation to overriding methods is not possible during index update");
return;
@@ -331,7 +340,7 @@ public class MarkerType {
private final PsiClass myClass;
private final PsiClassOrFunctionalExpressionListCellRenderer myRenderer;
public SubclassUpdater(PsiClass aClass, PsiClassOrFunctionalExpressionListCellRenderer renderer) {
private SubclassUpdater(@NotNull PsiClass aClass, @NotNull PsiClassOrFunctionalExpressionListCellRenderer renderer) {
super(aClass.getProject(), SEARCHING_FOR_OVERRIDDEN_METHODS);
myClass = aClass;
myRenderer = renderer;
@@ -374,14 +383,13 @@ public class MarkerType {
}
});
}
}
private static class OverridingMethodsUpdater extends ListBackgroundUpdaterTask {
private final PsiMethod myMethod;
private final PsiElementListCellRenderer myRenderer;
public OverridingMethodsUpdater(PsiMethod method, PsiElementListCellRenderer renderer) {
private OverridingMethodsUpdater(@NotNull PsiMethod method, @NotNull PsiElementListCellRenderer renderer) {
super(method.getProject(), SEARCHING_FOR_OVERRIDING_METHODS);
myMethod = method;
myRenderer = renderer;
@@ -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.
@@ -29,15 +29,11 @@ import com.intellij.psi.impl.FindSuperElementsHelper;
import com.intellij.psi.presentation.java.SymbolPresentationUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
public class ShowSiblingsAction extends ShowImplementationsAction {
public ShowSiblingsAction() {
super();
}
@Override
public void performForContext(DataContext dataContext, final boolean invokedByShortcut) {
public void performForContext(@NotNull DataContext dataContext, final boolean invokedByShortcut) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
final PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext);
@@ -61,7 +57,7 @@ public class ShowSiblingsAction extends ShowImplementationsAction {
}
final NavigatablePsiElement[] superElements = (NavigatablePsiElement[])findSuperElements(element);
if (superElements == null || superElements.length == 0) return;
if (superElements.length == 0) return;
final boolean isMethod = superElements[0] instanceof PsiMethod;
final JBPopup popup = PsiElementListNavigator.navigateOrCreatePopup(superElements, "Choose super " + (isMethod ? "method" : "class or interface"), "Super " + (isMethod ? "methods" : "classes/interfaces"),
@@ -81,11 +77,11 @@ public class ShowSiblingsAction extends ShowImplementationsAction {
}
private void showSiblings(boolean invokedByShortcut,
Project project,
@NotNull Project project,
Editor editor,
PsiFile file,
boolean invokedFromEditor,
PsiElement element) {
@NotNull PsiElement element) {
final PsiElement[] impls = getSelfAndImplementations(editor, element, createImplementationsSearcher(), false);
final String text = SymbolPresentationUtil.getSymbolPresentableText(element);
showImplementations(impls, project, text, editor, file, element, invokedFromEditor, invokedByShortcut);
@@ -96,11 +92,11 @@ public class ShowSiblingsAction extends ShowImplementationsAction {
return false;
}
@Nullable
@NotNull
private static PsiElement[] findSuperElements(final PsiElement element) {
PsiNameIdentifierOwner parent = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class);
if (parent == null) {
return null;
return PsiElement.EMPTY_ARRAY;
}
return FindSuperElementsHelper.findSuperElements(parent);
@@ -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.
@@ -32,7 +32,6 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class JavaGotoSuperHandler implements CodeInsightActionHandler {
@Override
@@ -41,7 +40,7 @@ public class JavaGotoSuperHandler implements CodeInsightActionHandler {
int offset = editor.getCaretModel().getOffset();
PsiElement[] superElements = findSuperElements(file, offset);
if (superElements == null || superElements.length == 0) return;
if (superElements.length == 0) return;
if (superElements.length == 1) {
PsiElement superElement = superElements[0].getNavigationElement();
final PsiFile containingFile = superElement.getContainingFile();
@@ -50,24 +49,25 @@ public class JavaGotoSuperHandler implements CodeInsightActionHandler {
if (virtualFile == null) return;
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile, superElement.getTextOffset());
FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
} else {
if (superElements[0] instanceof PsiMethod) {
boolean showMethodNames = !PsiUtil.allMethodsHaveSameSignature((PsiMethod[])superElements);
PsiElementListNavigator.openTargets(editor, (PsiMethod[])superElements,
CodeInsightBundle.message("goto.super.method.chooser.title"),
CodeInsightBundle.message("goto.super.method.findUsages.title", ((PsiMethod)superElements[0]).getName()),
new MethodCellRenderer(showMethodNames));
}
else {
NavigationUtil.getPsiElementPopup(superElements, CodeInsightBundle.message("goto.super.class.chooser.title")).showInBestPositionFor(editor);
}
}
else if (superElements[0] instanceof PsiMethod) {
boolean showMethodNames = !PsiUtil.allMethodsHaveSameSignature((PsiMethod[])superElements);
PsiElementListNavigator.openTargets(editor, (PsiMethod[])superElements,
CodeInsightBundle.message("goto.super.method.chooser.title"),
CodeInsightBundle
.message("goto.super.method.findUsages.title", ((PsiMethod)superElements[0]).getName()),
new MethodCellRenderer(showMethodNames));
}
else {
NavigationUtil.getPsiElementPopup(superElements, CodeInsightBundle.message("goto.super.class.chooser.title"))
.showInBestPositionFor(editor);
}
}
@Nullable
private PsiElement[] findSuperElements(PsiFile file, int offset) {
@NotNull
private PsiElement[] findSuperElements(@NotNull PsiFile file, int offset) {
PsiElement element = getElement(file, offset);
if (element == null) return null;
if (element == null) return PsiElement.EMPTY_ARRAY;
final PsiElement psiElement = PsiTreeUtil.getParentOfType(element, PsiFunctionalExpression.class, PsiMember.class);
if (psiElement instanceof PsiFunctionalExpression) {
@@ -79,13 +79,13 @@ public class JavaGotoSuperHandler implements CodeInsightActionHandler {
final PsiNameIdentifierOwner parent = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class, PsiClass.class);
if (parent == null) {
return null;
return PsiElement.EMPTY_ARRAY;
}
return FindSuperElementsHelper.findSuperElements(parent);
}
protected PsiElement getElement(PsiFile file, int offset) {
protected PsiElement getElement(@NotNull PsiFile file, int offset) {
return file.findElementAt(offset);
}
@@ -50,7 +50,7 @@ public class FormatPostfixTemplate extends StringBasedPostfixTemplate {
}
@Override
protected boolean shouldRemoveParent() {
return false;
protected PsiElement getElementToRemove(PsiElement expr) {
return expr;
}
}
@@ -36,6 +36,7 @@ import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.LinkedMultiMap;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -151,11 +152,11 @@ public class AddMethodsDialog extends DialogWrapper {
final DefaultComboBoxModel comboBoxModel = (DefaultComboBoxModel)myMethodNameCombo.getModel();
comboBoxModel.removeAllElements();
if (aClass == null) {
myMethodNameCombo.setEnabled(false);
enable(false);
}
else {
final List<PseudoLambdaReplaceTemplate> possibleTemplates = PseudoLambdaReplaceTemplate.getAllTemplates();
final MultiMap<String, PsiMethod> nameToMethod = new MultiMap<String, PsiMethod>();
final LinkedMultiMap<String, PsiMethod> nameToMethod = new LinkedMultiMap<String, PsiMethod>();
for (PsiMethod m : ContainerUtil.filter(aClass.getMethods(), new Condition<PsiMethod>() {
@Override
public boolean value(PsiMethod method) {
@@ -181,15 +182,27 @@ public class AddMethodsDialog extends DialogWrapper {
for (Map.Entry<String, Collection<PsiMethod>> entry : nameToMethod.entrySet()) {
comboBoxModel.addElement(entry.getValue());
}
myMethodNameCombo.setEnabled(true);
final boolean isSuitableMethodsFound = comboBoxModel.getSize() != 0;
enable(isSuitableMethodsFound);
}
}
});
myExamplePanel.setEnabled(false);
setOKActionEnabled(false);
init();
}
private void enable(boolean isEnabled) {
myMethodNameCombo.setEnabled(isEnabled);
myTemplatesCombo.setEnabled(isEnabled);
setOKActionEnabled(isEnabled);
myExamplePanel.setEnabled(isEnabled);
if (!isEnabled) {
myBeforeActionPanel.reset("", StdFileTypes.JAVA);
myAfterActionPanel.reset("", StdFileTypes.JAVA);
}
}
private void showTemplateExample(final PseudoLambdaReplaceTemplate template, final PsiMethod method) {
final PsiClass aClass = method.getContainingClass();
LOG.assertTrue(aClass != null);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 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.
@@ -26,6 +26,7 @@ import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.impl.FindSuperElementsHelper;
import com.intellij.psi.presentation.java.SymbolPresentationUtil;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.search.searches.DeepestSuperMethodsSearch;
@@ -34,6 +35,7 @@ import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -41,20 +43,24 @@ public class SuperMethodWarningUtil {
private SuperMethodWarningUtil() {}
@NotNull
public static PsiMethod[] checkSuperMethods(final PsiMethod method, String actionString) {
return checkSuperMethods(method, actionString, null);
public static PsiMethod[] checkSuperMethods(@NotNull PsiMethod method, @NotNull String actionString) {
return checkSuperMethods(method, actionString, Collections.<PsiElement>emptyList());
}
@NotNull
public static PsiMethod[] checkSuperMethods(final PsiMethod method, String actionString, Collection<PsiElement> ignore) {
public static PsiMethod[] checkSuperMethods(@NotNull PsiMethod method, @NotNull String actionString, @NotNull Collection<PsiElement> ignore) {
PsiClass aClass = method.getContainingClass();
if (aClass == null) return new PsiMethod[]{method};
final Collection<PsiMethod> superMethods = DeepestSuperMethodsSearch.search(method).findAll();
if (ignore != null) {
superMethods.removeAll(ignore);
}
superMethods.removeAll(ignore);
if (superMethods.isEmpty()) {
PsiMethod siblingSuperMethod = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method);
if (siblingSuperMethod != null) {
superMethods.add(siblingSuperMethod);
}
}
if (superMethods.isEmpty()) return new PsiMethod[]{method};
@@ -85,7 +91,7 @@ public class SuperMethodWarningUtil {
}
public static PsiMethod checkSuperMethod(final PsiMethod method, String actionString) {
public static PsiMethod checkSuperMethod(@NotNull PsiMethod method, @NotNull String actionString) {
PsiClass aClass = method.getContainingClass();
if (aClass == null) return method;
@@ -110,10 +116,10 @@ public class SuperMethodWarningUtil {
return null;
}
public static void checkSuperMethod(final PsiMethod method,
final String actionString,
final PsiElementProcessor<PsiMethod> processor,
final Editor editor) {
public static void checkSuperMethod(@NotNull PsiMethod method,
@NotNull String actionString,
@NotNull final PsiElementProcessor<PsiMethod> processor,
@NotNull Editor editor) {
PsiClass aClass = method.getContainingClass();
if (aClass == null) {
processor.execute(method);
@@ -137,7 +143,7 @@ public class SuperMethodWarningUtil {
return;
}
final PsiMethod[] methods = new PsiMethod[]{superMethod, method};
final PsiMethod[] methods = {superMethod, method};
final String renameBase = actionString + " base method";
final String renameCurrent = actionString + " only current method";
final JBList list = new JBList(renameBase, renameCurrent);
@@ -148,6 +154,7 @@ public class SuperMethodWarningUtil {
.setResizable(false)
.setRequestFocus(true)
.setItemChoosenCallback(new Runnable() {
@Override
public void run() {
final Object value = list.getSelectedValue();
if (value instanceof String) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 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.
@@ -20,13 +20,14 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.spi.psi.SPIClassProviderReferenceElement;
import org.jetbrains.annotations.NotNull;
/**
* User: anna
*/
public class SPIGotoSuperHandler extends JavaGotoSuperHandler {
@Override
protected PsiElement getElement(PsiFile file, int offset) {
protected PsiElement getElement(@NotNull PsiFile file, int offset) {
final SPIClassProviderReferenceElement
providerElement = PsiTreeUtil.getParentOfType(super.getElement(file, offset), SPIClassProviderReferenceElement.class);
if (providerElement != null) {
@@ -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.
@@ -61,7 +61,7 @@ public class JavaAllOverridingMethodsSearcher implements QueryExecutor<Pair<PsiM
PsiSubstitutor substitutor = null;
for (String name : methods.keySet()) {
if (inheritor.findMethodsByName(name, false).length == 0) continue;
if (inheritor.findMethodsByName(name, true).length == 0) continue;
for (PsiMethod method : methods.get(name)) {
if (method.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) &&
@@ -1,56 +0,0 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiSuperMethodUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class FindSuperElementsHelper {
@Nullable
public static PsiElement[] findSuperElements(@NotNull PsiElement element) {
if (element instanceof PsiClass) {
PsiClass aClass = (PsiClass) element;
List<PsiClass> allSupers = new ArrayList<PsiClass>(Arrays.asList(aClass.getSupers()));
for (Iterator<PsiClass> iterator = allSupers.iterator(); iterator.hasNext();) {
PsiClass superClass = iterator.next();
if (CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) iterator.remove();
}
return allSupers.toArray(new PsiClass[allSupers.size()]);
} else if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod) element;
if (method.isConstructor()) {
PsiMethod constructorInSuper = PsiSuperMethodUtil.findConstructorInSuper(method);
if (constructorInSuper != null) {
return new PsiMethod[]{constructorInSuper};
}
} else {
return method.findSuperMethods(false);
}
}
return null;
}
}
@@ -0,0 +1,11 @@
package z;
interface I {
void <caret>run();
}
abstract class A {
public void run() {}
}
class Foo extends A implements I {
}
@@ -0,0 +1,11 @@
package z;
interface I {
void run();
}
abstract class A {
public void <caret>run() {}
}
class Foo extends A implements I {
}
@@ -13,20 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.navigation;
package com.intellij.codeInsight.daemon.impl;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.CodeInsightActionHandler;
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
import com.intellij.ide.DataManager;
import com.intellij.lang.CodeInsightActions;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.actionSystem.Shortcut;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -38,7 +39,7 @@ public class JavaGotoSuperTest extends LightDaemonAnalyzerTestCase {
return JavaTestUtil.getJavaTestDataPath();
}
protected String getBasePath() {
private static String getBasePath() {
return "/codeInsight/gotosuper/";
}
@@ -46,6 +47,13 @@ public class JavaGotoSuperTest extends LightDaemonAnalyzerTestCase {
doTest();
}
private void doTest() {
configureByFile(getBasePath() + getTestName(false) + ".java");
final CodeInsightActionHandler handler = CodeInsightActions.GOTO_SUPER.forLanguage(JavaLanguage.INSTANCE);
handler.invoke(getProject(), getEditor(), getFile());
checkResultByFile(getBasePath() + getTestName(false) + ".after.java");
}
public void testLambdaMarker() throws Exception {
configureByFile(getBasePath() + getTestName(false) + ".java");
int offset = myEditor.getCaretModel().getOffset();
@@ -67,10 +75,52 @@ public class JavaGotoSuperTest extends LightDaemonAnalyzerTestCase {
fail("Gutter expected");
}
private void doTest() throws Throwable {
configureByFile(getBasePath() + getTestName(false) + ".java");
final CodeInsightActionHandler handler = CodeInsightActions.GOTO_SUPER.forLanguage(JavaLanguage.INSTANCE);
handler.invoke(getProject(), getEditor(), getFile());
checkResultByFile(getBasePath() + getTestName(false) + ".after.java");
public void testSiblingInheritance() throws Throwable {
doTest();
}
public void testSiblingInheritanceLineMarkers() throws Throwable {
configureByFile(getBasePath() + "SiblingInheritance.java");
PsiJavaFile file = (PsiJavaFile)getFile();
PsiClass i = JavaPsiFacade.getInstance(getProject()).findClass("z.I", GlobalSearchScope.fileScope(file));
PsiClass a = JavaPsiFacade.getInstance(getProject()).findClass("z.A", GlobalSearchScope.fileScope(file));
PsiMethod iRun = i.getMethods()[0];
assertEquals("run", iRun.getName());
PsiMethod aRun = a.getMethods()[0];
assertEquals("run", aRun.getName());
doHighlighting();
Document document = getEditor().getDocument();
List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
assertTrue(markers.size() >= 2);
LineMarkerInfo iMarker = findMarkerWithElement(markers, iRun.getNameIdentifier());
assertSame(MarkerType.OVERRIDDEN_METHOD.getNavigationHandler(), iMarker.getNavigationHandler());
LineMarkerInfo aMarker = findMarkerWithElement(markers, aRun.getNameIdentifier());
assertSame(MarkerType.OVERRIDING_METHOD.getNavigationHandler(), aMarker.getNavigationHandler());
}
private static LineMarkerInfo findMarkerWithElement(List<LineMarkerInfo> markers, PsiElement psiMethod) {
LineMarkerInfo marker = ContainerUtil.find(markers, info -> {
return info.getElement().equals(psiMethod);
});
assertNotNull(markers.toString(), marker);
return marker;
}
public void testSiblingInheritanceGoDown() throws Throwable {
configureByFile(getBasePath() + "SiblingInheritance.after.java");
AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_GOTO_IMPLEMENTATION);
AnActionEvent event = new AnActionEvent(
null,
DataManager.getInstance().getDataContextFromFocus().getResultSync(),
"",
action.getTemplatePresentation(),
ActionManager.getInstance(),
0);
action.update(event);
assertTrue(event.getPresentation().isEnabledAndVisible());
action.actionPerformed(event);
checkResultByFile(getBasePath() + "SiblingInheritance.java");
}
}
@@ -27,13 +27,13 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.cache.CacheManager;
import com.intellij.psi.impl.cache.TodoCacheManager;
import com.intellij.psi.impl.cache.impl.id.IdIndex;
import com.intellij.psi.impl.cache.impl.todo.TodoIndex;
import com.intellij.psi.search.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.TodoAttributesUtil;
import com.intellij.psi.search.TodoPattern;
import com.intellij.psi.search.UsageSearchContext;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.indexing.FileBasedIndex;
import java.io.File;
import java.util.Arrays;
@@ -48,9 +48,6 @@ public class IdCacheTest extends CodeInsightTestCase{
protected void setUp() throws Exception {
super.setUp();
FileBasedIndex.getInstance().requestRebuild(IdIndex.NAME);
FileBasedIndex.getInstance().requestRebuild(TodoIndex.NAME);
String root = JavaTestUtil.getJavaTestDataPath()+ "/psi/impl/cache/";
PsiTestUtil.removeAllRoots(myModule, IdeaTestUtil.getMockJdk17());
@@ -34,7 +34,6 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.JavaPsiFacadeEx;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.cache.impl.id.IdIndex;
import com.intellij.psi.impl.cache.impl.todo.TodoIndex;
import com.intellij.psi.impl.cache.impl.todo.TodoIndexEntry;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl;
@@ -52,15 +51,7 @@ import java.io.File;
import java.util.*;
@PlatformTestCase.WrapInCommand
public class UpdateCacheTest extends PsiTestCase{
@Override
protected void setUp() throws Exception {
super.setUp();
FileBasedIndex.getInstance().requestRebuild(IdIndex.NAME);
FileBasedIndex.getInstance().requestRebuild(TodoIndex.NAME);
}
public class UpdateCacheTest extends PsiTestCase {
@Override
protected void setUpProject() throws Exception {
myProjectManager = ProjectManagerEx.getInstanceEx();
+11
View File
@@ -0,0 +1,11 @@
<root>
<item name='org.hamcrest.SelfDescribing void describeTo(org.hamcrest.Description) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='org.hamcrest.TypeSafeDiagnosingMatcher boolean matchesSafely(T, org.hamcrest.Description) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='org.hamcrest.TypeSafeDiagnosingMatcher boolean matchesSafely(T, org.hamcrest.Description) 1'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
</root>
@@ -17,6 +17,7 @@ import org.jetbrains.io.ChannelExceptionHandler
import org.jetbrains.io.Decoder
import org.jetbrains.io.MessageDecoder
import org.jetbrains.io.NettyUtil
import org.jetbrains.testFramework.FixtureRule
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
@@ -1,19 +1,19 @@
package org.jetbrains.ide
import com.google.gson.stream.JsonWriter
import com.intellij.openapi.vfs.CharsetToolkit
import io.netty.handler.codec.http.HttpResponseStatus
import org.hamcrest.CoreMatchers.equalTo
import org.jetbrains.ide.TestManager.TestDescriptor
import org.jetbrains.testFramework.FixtureRule
import org.junit.Assert.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.hamcrest.CoreMatchers.equalTo
import org.jetbrains.ide.TestManager.TestDescriptor
import org.junit.Assert.assertThat
import java.net.URL
import java.net.HttpURLConnection
import com.google.gson.stream.JsonWriter
import java.io.OutputStreamWriter
import com.intellij.openapi.vfs.CharsetToolkit
import java.io.BufferedOutputStream
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
public class RestApiTest {
private val fixtureManager = FixtureRule()
@@ -1,5 +1,7 @@
package org.jetbrains.ide
import com.intellij.openapi.application.invokeAndWaitIfNeed
import com.intellij.openapi.application.writeAction
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.util.io.FileUtilRt
@@ -63,10 +65,10 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() {
}
invokeAndWaitIfNeed {
val normalizedFilePath = FileUtilRt.toSystemIndependentName(filePath)
val normalizedFilePath = FileUtilRt.toSystemIndependentName(filePath!!)
if (annotation!!.relativeToProject) {
val root = projectFixture.getProject().getBaseDir()
runWriteAction {
writeAction {
fileToDelete = root.findOrCreateChildData(this@TestManager, normalizedFilePath)
}
}
@@ -75,7 +77,7 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() {
ModuleRootModificationUtil.updateModel(projectFixture.getModule()) { model ->
val contentEntry = model.getContentEntries()[0]
val contentRoot = contentEntry.getFile()!!
runWriteAction {
writeAction {
contentRoot.findChild(EXCLUDED_DIR_NAME)?.delete(this@TestManager)
fileToDelete = contentRoot.createChildDirectory(this@TestManager, EXCLUDED_DIR_NAME)
fileToDelete!!.createChildData(this@TestManager, normalizedFilePath)
@@ -87,7 +89,7 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() {
}
else {
val root = ModuleRootManager.getInstance(projectFixture.getModule()).getSourceRoots()[0]
runWriteAction {
writeAction {
fileToDelete = root.findOrCreateChildData(this@TestManager, normalizedFilePath)
}
}
@@ -101,11 +103,11 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() {
}
if (fileToDelete != null) {
invokeAndWaitIfNeed { runWriteAction { fileToDelete?.delete(this@TestManager) } }
invokeAndWaitIfNeed { writeAction { fileToDelete?.delete(this@TestManager) } }
fileToDelete = null
}
if (ioFileToDelete != null && !FileUtilRt.delete(ioFileToDelete)) {
if (ioFileToDelete != null && !FileUtilRt.delete(ioFileToDelete!!)) {
ioFileToDelete!!.deleteOnExit()
}
@@ -140,6 +140,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected {
*
* @return the new marker instance.
*/
@NotNull
Marker precede();
/**
@@ -160,7 +161,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected {
*
* @param type the type of the node in the AST tree.
*/
void done(IElementType type);
void done(@NotNull IElementType type);
/**
* Like {@linkplain #done(IElementType)}, but collapses all tokens between start and end markers
@@ -168,7 +169,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected {
*
* @param type the type of the node in the AST tree.
*/
void collapse(IElementType type);
void collapse(@NotNull IElementType type);
/**
* Like {@linkplain #done(IElementType)}, but the marker is completed (end marker inserted)
@@ -178,7 +179,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected {
* @param type the type of the node in the AST tree.
* @param before marker to complete this one before.
*/
void doneBefore(IElementType type, Marker before);
void doneBefore(@NotNull IElementType type, @NotNull Marker before);
/**
* Like {@linkplain #doneBefore(IElementType, Marker)}, but in addition an error element with given text
@@ -188,7 +189,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected {
* @param before marker to complete this one before.
* @param errorMessage for error element.
*/
void doneBefore(IElementType type, Marker before, String errorMessage);
void doneBefore(@NotNull IElementType type, @NotNull Marker before, String errorMessage);
/**
* Completes this marker and labels it as error element with specified message. Before calling this method,
@@ -204,7 +205,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected {
* @param message for error element.
* @param before marker to complete this one before.
*/
void errorBefore(String message, Marker before);
void errorBefore(String message, @NotNull Marker before);
/**
* Allows to define custom edge token binders instead of default ones. If any of parameters is null
@@ -222,6 +223,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected {
*
* @return the new marker instance.
*/
@NotNull
Marker mark();
/**
@@ -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.
@@ -38,5 +38,5 @@ public interface PsiParser {
* @return the root of the resulting AST tree.
*/
@NotNull
ASTNode parse(IElementType root, PsiBuilder builder);
ASTNode parse(@NotNull IElementType root, @NotNull PsiBuilder builder);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 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.
@@ -16,6 +16,7 @@
package com.intellij.lang;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -31,6 +32,7 @@ public interface WhitespacesAndCommentsBinder {
* Provides an ability for the processor to get a text of any of given tokens.
*/
interface TokenTextGetter {
@NotNull
CharSequence get(int i);
}
@@ -46,8 +46,6 @@ public abstract class SchemesManager<T extends Scheme, E extends ExternalizableS
@Nullable
public abstract T findSchemeByName(@NotNull String schemeName);
public abstract void save();
public abstract void setCurrentSchemeName(@Nullable String schemeName);
@Nullable
@@ -60,6 +58,9 @@ public abstract class SchemesManager<T extends Scheme, E extends ExternalizableS
public abstract File getRootDirectory();
/**
* Must be called before {@link #loadSchemes}
*/
public void loadBundledScheme(@NotNull String resourceName, @NotNull Object requestor, @NotNull ThrowableConvertor<Element, T, Throwable> convertor) {
}
@@ -69,4 +70,11 @@ public abstract class SchemesManager<T extends Scheme, E extends ExternalizableS
public void setSchemes(@NotNull List<T> schemes, @Nullable Condition<T> removeCondition) {
}
/**
* Bundled / read-only (or overriding) scheme cannot be renamed or deleted.
*/
public boolean isMetadataEditable(@NotNull E scheme) {
return true;
}
}
+1
View File
@@ -11,5 +11,6 @@
<orderEntry type="library" name="picocontainer" level="project" />
<orderEntry type="module" module-name="boot" />
<orderEntry type="library" name="Guava" level="project" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>
@@ -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.
@@ -35,6 +35,7 @@ public abstract class DelegateMarker implements PsiBuilder.Marker {
return myDelegate;
}
@NotNull
@Override
public PsiBuilder.Marker precede() {
return myDelegate.precede();
@@ -51,22 +52,22 @@ public abstract class DelegateMarker implements PsiBuilder.Marker {
}
@Override
public void done(IElementType type) {
public void done(@NotNull IElementType type) {
myDelegate.done(type);
}
@Override
public void collapse(IElementType type) {
public void collapse(@NotNull IElementType type) {
myDelegate.collapse(type);
}
@Override
public void doneBefore(IElementType type, PsiBuilder.Marker before) {
public void doneBefore(@NotNull IElementType type, @NotNull PsiBuilder.Marker before) {
myDelegate.doneBefore(type, before);
}
@Override
public void doneBefore(IElementType type, PsiBuilder.Marker before, String errorMessage) {
public void doneBefore(@NotNull IElementType type, @NotNull PsiBuilder.Marker before, String errorMessage) {
myDelegate.doneBefore(type, before, errorMessage);
}
@@ -76,7 +77,7 @@ public abstract class DelegateMarker implements PsiBuilder.Marker {
}
@Override
public void errorBefore(String message, PsiBuilder.Marker before) {
public void errorBefore(String message, @NotNull PsiBuilder.Marker before) {
myDelegate.errorBefore(message, before);
}
@@ -101,6 +101,7 @@ public class PsiBuilderAdapter implements PsiBuilder {
return myDelegate.getCurrentOffset();
}
@NotNull
@Override
public Marker mark() {
return myDelegate.mark();
@@ -0,0 +1,38 @@
/*
* 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.application
import javax.swing.SwingUtilities
public inline fun writeAction(runnable: () -> Unit) {
val token = WriteAction.start()
try {
runnable()
}
finally {
token.finish()
}
}
public fun invokeAndWaitIfNeed(runnable: () -> Unit) {
val app = ApplicationManager.getApplication()
if (app == null) {
if (SwingUtilities.isEventDispatchThread()) runnable() else SwingUtilities.invokeAndWait(runnable)
}
else {
if (app.isDispatchThread()) runnable() else app.invokeAndWait(runnable, ModalityState.any())
}
}
@@ -48,10 +48,6 @@ public class EmptySchemesManager extends SchemesManager {
return null;
}
@Override
public void save() {
}
@Override
public void setCurrentSchemeName(String schemeName) {
}
@@ -178,14 +178,15 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi
psiFile = PsiUtilCore.NULL_PSI_FILE;
}
boolean set = myPsiFile.compareAndSet(null, psiFile);
if (!set) {
if (!set && psiFile != PsiUtilCore.NULL_PSI_FILE) {
PsiFile alreadyCreated = myPsiFile.get();
if (alreadyCreated == psiFile) {
LOG.error(this + ".createFile() must create new file instance but got the same: " + psiFile);
}
if (psiFile instanceof PsiFileImpl) {
if (myPsiFile.get() == psiFile) {
LOG.error(this + ".createFile() must create new file instance but got the same: " + psiFile);
}
((PsiFileImpl)psiFile).markInvalidated();
}
psiFile = myPsiFile.get();
psiFile = alreadyCreated;
}
}
return psiFile == PsiUtilCore.NULL_PSI_FILE ? null : psiFile;
@@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ShortcutSet;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.event.MouseEvent;
@@ -28,10 +29,10 @@ import java.awt.event.MouseEvent;
* @author Dmitry Avdeev
*/
public class NavigateAction<T extends PsiElement> extends AnAction {
private LineMarkerInfo<T> myInfo;
private final LineMarkerInfo<T> myInfo;
public NavigateAction(String text,
LineMarkerInfo<T> info,
public NavigateAction(@NotNull String text,
@NotNull LineMarkerInfo<T> info,
@Nullable String originalActionId) {
super(text);
myInfo = info;
@@ -41,7 +42,7 @@ public class NavigateAction<T extends PsiElement> extends AnAction {
}
}
public NavigateAction(LineMarkerInfo<T> info) {
public NavigateAction(@NotNull LineMarkerInfo<T> info) {
myInfo = info;
}
@@ -56,7 +57,8 @@ public class NavigateAction<T extends PsiElement> extends AnAction {
}
}
public static <T extends PsiElement> LineMarkerInfo<T> setNavigateAction(LineMarkerInfo<T> info, String text, @Nullable String originalActionId) {
@NotNull
public static <T extends PsiElement> LineMarkerInfo<T> setNavigateAction(@NotNull LineMarkerInfo<T> info, @NotNull String text, @Nullable String originalActionId) {
NavigateAction<T> action = new NavigateAction<T>(text, info, originalActionId);
info.setNavigateAction(action);
return info;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 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.
@@ -37,6 +37,7 @@ import com.intellij.ui.popup.HintUpdateSupply;
import com.intellij.usages.UsageView;
import com.intellij.util.Consumer;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
@@ -88,12 +89,12 @@ public class PsiElementListNavigator {
}
@Nullable
public static JBPopup navigateOrCreatePopup(final NavigatablePsiElement[] targets,
public static JBPopup navigateOrCreatePopup(@NotNull final NavigatablePsiElement[] targets,
final String title,
final String findUsagesTitle,
final ListCellRenderer listRenderer,
@Nullable final ListBackgroundUpdaterTask listUpdaterTask,
final Consumer<Object[]> consumer) {
@NotNull final Consumer<Object[]> consumer) {
if (targets.length == 0) return null;
if (targets.length == 1) {
consumer.consume(targets);
@@ -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.
@@ -105,7 +105,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
}
protected Editor getEditor(DataContext dataContext) {
protected static Editor getEditor(@NotNull DataContext dataContext) {
Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
if (editor == null) {
@@ -123,7 +123,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
return editor;
}
public void performForContext(DataContext dataContext, boolean invokedByShortcut) {
public void performForContext(@NotNull DataContext dataContext, boolean invokedByShortcut) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project == null) return;
PsiDocumentManager.getInstance(project).commitAllDocuments();
@@ -185,7 +185,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
showImplementations(impls, project, text, editor, file, element, isInvokedFromEditor, invokedByShortcut);
}
protected static PsiElement getElement(Project project, PsiFile file, Editor editor, PsiElement element) {
protected static PsiElement getElement(@NotNull Project project, PsiFile file, Editor editor, PsiElement element) {
if (element == null && editor != null) {
element = TargetElementUtil.findTargetElement(editor, TargetElementUtil.getInstance().getAllAccepted());
final PsiElement adjustedElement =
@@ -200,6 +200,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
return element;
}
@NotNull
protected static ImplementationSearcher createImplementationsSearcher() {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return new ImplementationSearcher() {
@@ -209,18 +210,16 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
}
};
}
else {
return new ImplementationSearcher.FirstImplementationsSearcher() {
@Override
protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements, final int offset) {
return ShowImplementationsAction.filterElements(targetElements);
}
};
}
return new ImplementationSearcher.FirstImplementationsSearcher() {
@Override
protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements, final int offset) {
return ShowImplementationsAction.filterElements(targetElements);
}
};
}
protected void updateElementImplementations(final PsiElement element, final Editor editor, final Project project, final PsiFile file) {
PsiElement[] impls = null;
private void updateElementImplementations(final PsiElement element, final Editor editor, @NotNull Project project, final PsiFile file) {
PsiElement[] impls = {};
String text = "";
if (element != null) {
// if (element instanceof PsiPackage) return;
@@ -234,10 +233,15 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
showImplementations(impls, project, text, editor, file, element, false, false);
}
protected void showImplementations(final PsiElement[] impls, final Project project, final String text, final Editor editor, final PsiFile file,
protected void showImplementations(@NotNull PsiElement[] impls,
@NotNull final Project project,
final String text,
final Editor editor,
final PsiFile file,
final PsiElement element,
boolean invokedFromEditor, boolean invokedByShortcut) {
if (impls == null || impls.length == 0) return;
boolean invokedFromEditor,
boolean invokedByShortcut) {
if (impls.length == 0) return;
FeatureUsageTracker.getInstance().triggerFeatureUsed(CODEASSISTS_QUICKDEFINITION_FEATURE);
if (LookupManager.getInstance(project).getActiveLookup() != null) {
@@ -317,7 +321,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
}
}
private static boolean cancelTask(ImplementationsUpdaterTask task) {
private static boolean cancelTask(@Nullable ImplementationsUpdaterTask task) {
if (task != null) {
ProgressIndicator indicator = task.myIndicator;
if (indicator != null) {
@@ -330,9 +334,10 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
private void updateInBackground(Editor editor,
@Nullable PsiElement element,
ImplementationViewComponent component,
@NotNull ImplementationViewComponent component,
String title,
AbstractPopup popup, Ref<UsageView> usageView) {
@NotNull AbstractPopup popup,
@NotNull Ref<UsageView> usageView) {
final ImplementationsUpdaterTask updaterTask = SoftReference.dereference(myTaskRef);
cancelTask(updaterTask);
@@ -348,15 +353,17 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
return true;
}
@NotNull
private static PsiElement[] getSelfAndImplementations(Editor editor,
PsiElement element,
final ImplementationSearcher handler) {
@NotNull PsiElement element,
@NotNull ImplementationSearcher handler) {
return getSelfAndImplementations(editor, element, handler, !(element instanceof PomTargetPsiElement));
}
@NotNull
protected static PsiElement[] getSelfAndImplementations(Editor editor,
PsiElement element,
final ImplementationSearcher handler,
@NotNull PsiElement element,
@NotNull ImplementationSearcher handler,
final boolean includeSelfAlways) {
int offset = editor == null ? 0 : editor.getCaretModel().getOffset();
final PsiElement[] handlerImplementations = handler.searchImplementations(element, editor, offset, includeSelfAlways, true);
@@ -367,17 +374,18 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
// Magically, it's null for ant property declarations.
element = element.getNavigationElement();
psiFile = element.getContainingFile();
if (psiFile == null) return PsiElement.EMPTY_ARRAY;
if (psiFile == null) {
return PsiElement.EMPTY_ARRAY;
}
}
if (psiFile.getVirtualFile() != null && (element.getTextRange() != null || element instanceof PsiFile)) {
return new PsiElement[]{element};
}
else {
return PsiElement.EMPTY_ARRAY;
}
return PsiElement.EMPTY_ARRAY;
}
private static PsiElement[] filterElements(final PsiElement[] targetElements) {
@NotNull
private static PsiElement[] filterElements(@NotNull final PsiElement[] targetElements) {
final Set<PsiElement> unique = new LinkedHashSet<PsiElement>(Arrays.asList(targetElements));
for (final PsiElement elt : targetElements) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@@ -410,12 +418,13 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
private static class ImplementationsUpdaterTask extends BackgroundUpdaterTask<ImplementationViewComponent> {
private final String myCaption;
private final Editor myEditor;
@NotNull
private final PsiElement myElement;
private final boolean myIncludeSelf;
private PsiElement[] myElements;
private volatile ProgressIndicator myIndicator;
public ImplementationsUpdaterTask(final PsiElement element, final Editor editor, final String caption, boolean includeSelf) {
private ImplementationsUpdaterTask(@NotNull PsiElement element, final Editor editor, final String caption, boolean includeSelf) {
super(element.getProject(), ImplementationSearcher.SEARCHING_FOR_IMPLEMENTATIONS);
myCaption = caption;
myEditor = editor;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,6 @@ import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupAdapter;
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.ui.popup.AbstractPopup;
@@ -69,7 +67,7 @@ public abstract class BackgroundUpdaterTask<T> extends Task.Backgroundable {
super(project, title, canBeCancelled, backgroundOption);
}
public void init(@NotNull AbstractPopup popup, T component, Ref<UsageView> usageView) {
public void init(@NotNull AbstractPopup popup, @NotNull T component, @NotNull Ref<UsageView> usageView) {
myPopup = popup;
myComponent = component;
myUsageView = usageView;
@@ -669,15 +669,6 @@ public class FindDialog extends DialogWrapper {
}
private void doOKAction(boolean findAll) {
if (myResultsPreviewTable != null &&
myContent.getSelectedIndex() == RESULTS_PREVIEW_TAB_INDEX &&
myResultsPreviewTable.getSelectedRowCount() != 0 &&
!myModel.isReplaceState()
) {
navigateToSelectedUsage(myResultsPreviewTable);
return;
}
FindModel validateModel = myModel.clone();
applyTo(validateModel, findAll);
@@ -1716,9 +1716,11 @@ public class FileBasedIndexImpl extends FileBasedIndex {
fc.putUserData(IndexingDataKeys.PROJECT, project);
}
static final Key<Boolean> ourPhysicalContentKey = Key.create("physical.content.flag");
private void updateSingleIndex(@NotNull ID<?, ?> indexId, final int inputId, @Nullable FileContent currentFC)
throws StorageException {
if (ourRebuildStatus.get(indexId).get() == REQUIRES_REBUILD) {
if (ourRebuildStatus.get(indexId).get() == REQUIRES_REBUILD && !myIsUnitTestMode) {
return; // the index is scheduled for rebuild, no need to update
}
myLocalModCount++;
@@ -1726,7 +1728,8 @@ public class FileBasedIndexImpl extends FileBasedIndex {
final UpdatableIndex<?, ?, FileContent> index = getIndex(indexId);
assert index != null;
if (currentFC != null && currentFC.getUserData(ourPhysicalContentKey) == null) {
boolean hasContent = currentFC != null;
if (hasContent && currentFC.getUserData(ourPhysicalContentKey) == null) {
currentFC.putUserData(ourPhysicalContentKey, Boolean.TRUE);
}
@@ -1735,10 +1738,7 @@ public class FileBasedIndexImpl extends FileBasedIndex {
final Computable<Boolean> update = index.update(inputId, currentFC);
try {
scheduleUpdate(indexId,
createUpdateComputableWithBufferingDisabled(update),
createIndexedStampUpdateRunnable(indexId, inputId, currentFC != null)
);
scheduleUpdate(indexId, update, inputId, hasContent);
} catch (RuntimeException exception) {
Throwable causeToRebuildIndex = getCauseToRebuildIndex(exception);
if (causeToRebuildIndex != null) {
@@ -1750,51 +1750,51 @@ public class FileBasedIndexImpl extends FileBasedIndex {
}
}
static final Key<Boolean> ourPhysicalContentKey = Key.create("physical.content.flag");
@NotNull
private Runnable createIndexedStampUpdateRunnable(@NotNull final ID<?, ?> indexId,
final int fileId,
final boolean hasContent) {
return new Runnable() {
@Override
public void run() {
if (hasContent) {
IndexingStamp.setFileIndexedStateCurrent(fileId, indexId);
}
else {
IndexingStamp.setFileIndexedStateUnindexed(fileId, indexId);
}
if (myNotRequiringContentIndices.contains(indexId)) IndexingStamp.flushCache(fileId);
}
};
}
@NotNull
private Computable<Boolean> createUpdateComputableWithBufferingDisabled(@NotNull final Computable<Boolean> update) {
return new Computable<Boolean>() {
@Override
public Boolean compute() {
Boolean result;
final StorageGuard.StorageModeExitHandler lock = setDataBufferingEnabled(false);
try {
result = update.compute();
}
finally {
lock.leave();
}
return result;
}
};
}
private void scheduleUpdate(@NotNull ID<?, ?> indexId, @NotNull Computable<Boolean> update, @NotNull Runnable successRunnable) {
private void scheduleUpdate(@NotNull final ID<?, ?> indexId, final Computable<Boolean> update, final int inputId, final boolean hasContent) {
if (myNotRequiringContentIndices.contains(indexId) && !Registry.is("idea.concurrent.scanning.files.to.index")) {
myContentlessIndicesUpdateQueue.submit(update, successRunnable);
myContentlessIndicesUpdateQueue.submit(
new Computable<Boolean>() {
@Override
public Boolean compute() {
return updateWithBufferingEnabled(update);
}
},
new Runnable() {
@Override
public void run() {
indexedStampUpdate(indexId, inputId, hasContent);
}
});
}
else {
Boolean result = update.compute();
if (result == Boolean.TRUE) ApplicationManager.getApplication().runReadAction(successRunnable);
if (updateWithBufferingEnabled(update)) {
AccessToken accessToken = ReadAction.start();
try {
indexedStampUpdate(indexId, inputId, hasContent);
} finally {
accessToken.finish();
}
}
}
}
protected void indexedStampUpdate(@NotNull ID<?, ?> indexId, int fileId, boolean hasContent) {
if (hasContent) {
IndexingStamp.setFileIndexedStateCurrent(fileId, indexId);
}
else {
IndexingStamp.setFileIndexedStateUnindexed(fileId, indexId);
}
if (myNotRequiringContentIndices.contains(indexId)) IndexingStamp.flushCache(fileId);
}
protected boolean updateWithBufferingEnabled(@NotNull final Computable<Boolean> update) {
final StorageGuard.StorageModeExitHandler lock = setDataBufferingEnabled(false);
try {
return update.compute();
}
finally {
lock.leave();
}
}
@@ -25,7 +25,7 @@ import java.util.Collection;
* @author traff
*/
public abstract class AbstractCollectionComboBoxModel<T> extends CollectionListModel<T> implements ComboBoxModel {
private T mySelection;
protected T mySelection;
public AbstractCollectionComboBoxModel(@Nullable T selection) {
mySelection = selection;
+1
View File
@@ -42,5 +42,6 @@
<orderEntry type="module" module-name="built-in-server" scope="RUNTIME" />
<orderEntry type="library" name="imgscalr" level="project" />
<orderEntry type="module" module-name="built-in-server-api" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>
@@ -43,11 +43,11 @@ public class QuickListsManager implements ExportableApplicationComponent {
private static final String LIST_TAG = "list";
private final ActionManager myActionManager;
private final SchemesManager<QuickList, QuickList> mySchemesManager;
private final SchemesManager<QuickList, QuickList> mySchemeManager;
public QuickListsManager(@NotNull ActionManager actionManager, @NotNull SchemesManagerFactory schemesManagerFactory) {
myActionManager = actionManager;
mySchemesManager = schemesManagerFactory.createSchemesManager(FILE_SPEC,
mySchemeManager = schemesManagerFactory.createSchemesManager(FILE_SPEC,
new BaseSchemeProcessor<QuickList>() {
@NotNull
@Override
@@ -73,7 +73,7 @@ public class QuickListsManager implements ExportableApplicationComponent {
@Override
@NotNull
public File[] getExportFiles() {
return new File[]{mySchemesManager.getRootDirectory()};
return new File[]{mySchemeManager.getRootDirectory()};
}
@NotNull
@@ -99,7 +99,7 @@ public class QuickListsManager implements ExportableApplicationComponent {
public void initComponent() {
for (BundledQuickListsProvider provider : BundledQuickListsProvider.EP_NAME.getExtensions()) {
for (final String path : provider.getBundledListsRelativePaths()) {
mySchemesManager.loadBundledScheme(path, provider, new ThrowableConvertor<Element, QuickList, Throwable>() {
mySchemeManager.loadBundledScheme(path, provider, new ThrowableConvertor<Element, QuickList, Throwable>() {
@Override
public QuickList convert(Element element) throws Throwable {
return createItem(element);
@@ -107,7 +107,7 @@ public class QuickListsManager implements ExportableApplicationComponent {
});
}
}
mySchemesManager.loadSchemes();
mySchemeManager.loadSchemes();
registerActions();
}
@@ -116,20 +116,20 @@ public class QuickListsManager implements ExportableApplicationComponent {
}
@NotNull
public List<QuickList> getQuickLists() {
return mySchemesManager.getAllSchemes();
public SchemesManager<QuickList, QuickList> getSchemeManager() {
return mySchemeManager;
}
@NotNull
public QuickList[] getAllQuickLists() {
Collection<QuickList> lists = mySchemesManager.getAllSchemes();
Collection<QuickList> lists = mySchemeManager.getAllSchemes();
return lists.toArray(new QuickList[lists.size()]);
}
private void registerActions() {
// to prevent exception if 2 or more targets have the same name
Set<String> registeredIds = new THashSet<String>();
for (QuickList list : mySchemesManager.getAllSchemes()) {
for (QuickList list : mySchemeManager.getAllSchemes()) {
String actionId = list.getActionId();
if (registeredIds.add(actionId)) {
myActionManager.registerAction(actionId, new InvokeQuickListAction(list));
@@ -145,7 +145,7 @@ public class QuickListsManager implements ExportableApplicationComponent {
public void setQuickLists(@NotNull List<QuickList> quickLists) {
unregisterActions();
mySchemesManager.setSchemes(quickLists);
mySchemeManager.setSchemes(quickLists);
registerActions();
}
@@ -1,89 +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.components.impl.stores;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.util.Condition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
public abstract class StreamProvider {
public static final StreamProvider[] EMPTY_ARRAY = new StreamProvider[0];
public boolean isEnabled() {
return true;
}
/**
* fileSpec Only main fileSpec, not version
*/
public boolean isApplicable(@NotNull String fileSpec, @NotNull RoamingType roamingType) {
return true;
}
/**
* @param fileSpec
* @param content bytes of content, size of array is not actual size of data, you must use {@code size}
* @param size actual size of data
*/
public abstract void saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType) throws IOException;
@Nullable
public abstract InputStream loadContent(@NotNull String fileSpec, @NotNull RoamingType roamingType) throws IOException;
@NotNull
public Collection<String> listSubFiles(@NotNull String fileSpec, @NotNull RoamingType roamingType) {
return Collections.emptyList();
}
/**
* You must close passed input stream.
*/
public void processChildren(@NotNull String path, @NotNull RoamingType roamingType, @NotNull Condition<String> filter, @NotNull ChildrenProcessor processor) {
for (String name : listSubFiles(path, roamingType)) {
if (!filter.value(name)) {
continue;
}
InputStream input;
try {
input = loadContent(path + '/' + name, roamingType);
}
catch (IOException e) {
StorageUtil.LOG.error(e);
continue;
}
if (input != null && !processor.process(name, input)) {
break;
}
}
}
public abstract static class ChildrenProcessor {
public abstract boolean process(@NotNull String name, @NotNull InputStream input);
}
/**
* Delete file or directory
*/
public abstract void delete(@NotNull String fileSpec, @NotNull RoamingType roamingType);
}
@@ -0,0 +1,72 @@
/*
* 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.components.impl.stores
import com.intellij.openapi.components.RoamingType
import java.io.IOException
import java.io.InputStream
public interface StreamProvider {
public open fun isEnabled(): Boolean = true
/**
* fileSpec Only main fileSpec, not version
*/
public open fun isApplicable(fileSpec: String, roamingType: RoamingType): Boolean = true
/**
* @param fileSpec
* *
* @param content bytes of content, size of array is not actual size of data, you must use `size`
* *
* @param size actual size of data
*/
public fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType)
public fun loadContent(fileSpec: String, roamingType: RoamingType): InputStream?
public open fun listSubFiles(fileSpec: String, roamingType: RoamingType): Collection<String> = emptyList()
/**
* You must close passed input stream.
*/
public open fun processChildren(path: String, roamingType: RoamingType, filter: (name: String) -> Boolean, processor: (name: String, input: InputStream, readOnly: Boolean) -> Boolean) {
for (name in listSubFiles(path, roamingType)) {
if (!filter(name)) {
continue
}
val input: InputStream?
try {
input = loadContent("$path/$name", roamingType)
}
catch (e: IOException) {
StorageUtil.LOG.error(e)
continue
}
if (input != null && !processor(name, input, false)) {
break
}
}
}
/**
* Delete file or directory
*/
public fun delete(fileSpec: String, roamingType: RoamingType)
}
@@ -43,7 +43,13 @@ public class ComplementaryFontsRegistry {
private static FontInfo ourSharedDefaultFont;
private static final TIntHashSet ourUndisplayableChars = new TIntHashSet();
private static boolean ourOldUseAntialiasing;
// This matches style detection in JDK (class sun.font.Font2D)
private static final String[] BOLD_NAMES = {"bold", "demibold", "demi-bold", "demi bold", "negreta", "demi" };
private static final String[] ITALIC_NAMES = {"italic", "cursiva", "oblique", "inclined"};
private static final String[] BOLD_ITALIC_NAMES = {"bolditalic", "bold-italic", "bold italic", "boldoblique", "bold-oblique",
"bold oblique", "demibold italic", "negreta cursiva","demi oblique"};
static {
final UISettings settings = UISettings.getInstance();
ourOldUseAntialiasing = settings.ANTIALIASING_IN_EDITOR;
@@ -121,44 +127,36 @@ public class ComplementaryFontsRegistry {
Font[] allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
for (Font font : allFonts) {
String name = font.getName();
int style;
if (name.endsWith("-Italic")) {
style = Font.ITALIC;
}
else if (name.endsWith("-Bold")) {
style = Font.BOLD;
}
else if (name.endsWith("-BoldItalic")) {
style = Font.BOLD | Font.ITALIC;
}
else {
style = Font.PLAIN;
}
int style = getFontStyle(name);
if (style != Font.PLAIN) {
String baseName = name.substring(0, name.lastIndexOf('-'));
Pair<String, Integer>[] entry = ourStyledFontMap.get(baseName);
String familyName = font.getFamily();
Pair<String, Integer>[] entry = ourStyledFontMap.get(familyName);
if (entry == null) {
//noinspection unchecked
entry = new Pair[4];
for (int i = 1; i < 4; i++) {
entry[i] = Pair.create(baseName, i);
entry[i] = Pair.create(familyName, i);
}
ourStyledFontMap.put(baseName, entry);
ourStyledFontMap.put(familyName, entry);
}
entry[style] = Pair.create(name, Font.PLAIN);
}
}
}
private static Pair<String, Integer> fontFamily(String familyName, int style) {
if (SystemInfo.isMac && style > 0 && style < 4) {
Pair<String, Integer>[] replacement = ourStyledFontMap.get(familyName);
if (replacement != null) {
familyName = replacement[style].first;
style = replacement[style].second;
}
@JdkConstants.FontStyle
private static int getFontStyle(String fontName) {
fontName = fontName.toLowerCase(Locale.getDefault());
for (String name : BOLD_ITALIC_NAMES) {
if (fontName.contains(name)) return Font.BOLD | Font.ITALIC;
}
return Pair.create(familyName, style);
for (String name : ITALIC_NAMES) {
if (fontName.contains(name)) return Font.ITALIC;
}
for (String name : BOLD_NAMES) {
if (fontName.contains(name)) return Font.BOLD;
}
return Font.PLAIN;
}
@NotNull
@@ -200,11 +198,17 @@ public class ComplementaryFontsRegistry {
@Nullable
private static FontInfo doGetFontAbleToDisplay(char c, int size, @JdkConstants.FontStyle int style, @NotNull String defaultFontFamily) {
synchronized (lock) {
Pair<String, Integer> p = fontFamily(defaultFontFamily, style);
if (SystemInfo.isMac && style > 0 && style < 4) {
Pair<String, Integer>[] replacement = ourStyledFontMap.get(defaultFontFamily);
if (replacement != null) {
defaultFontFamily = replacement[style].first;
style = replacement[style].second;
}
}
if (ourSharedKeyInstance.mySize == size &&
ourSharedKeyInstance.myStyle == p.getSecond() &&
ourSharedKeyInstance.myStyle == style &&
ourSharedKeyInstance.myFamilyName != null &&
ourSharedKeyInstance.myFamilyName.equals(p.getFirst()) &&
ourSharedKeyInstance.myFamilyName.equals(defaultFontFamily) &&
ourSharedDefaultFont != null &&
( c < 128 ||
ourSharedDefaultFont.canDisplay(c)
@@ -213,13 +217,13 @@ public class ComplementaryFontsRegistry {
return ourSharedDefaultFont;
}
ourSharedKeyInstance.myFamilyName = p.getFirst();
ourSharedKeyInstance.myFamilyName = defaultFontFamily;
ourSharedKeyInstance.mySize = size;
ourSharedKeyInstance.myStyle = p.getSecond();
ourSharedKeyInstance.myStyle = style;
FontInfo defaultFont = ourUsedFonts.get(ourSharedKeyInstance);
if (defaultFont == null) {
defaultFont = new FontInfo(p.getFirst(), size, p.getSecond());
defaultFont = new FontInfo(defaultFontFamily, size, style);
ourUsedFonts.put(ourSharedKeyInstance, defaultFont);
ourSharedKeyInstance = new FontKey("", 0, 0);
}
@@ -30,7 +30,9 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.OrderedSet;
import gnu.trove.THashMap;
import gnu.trove.TObjectObjectProcedure;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -39,7 +41,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.lang.reflect.Field;
import java.util.*;
@@ -77,7 +78,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
private KeymapImpl myParent;
private boolean myCanModify = true;
private final Map<String, LinkedHashSet<Shortcut>> myActionId2ListOfShortcuts = new THashMap<String, LinkedHashSet<Shortcut>>();
private final THashMap<String, OrderedSet<Shortcut>> myActionId2ListOfShortcuts = new THashMap<String, OrderedSet<Shortcut>>();
/**
* Don't use this field directly! Use it only through <code>getKeystroke2ListOfIds</code>.
@@ -134,21 +135,26 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@NotNull
public KeymapImpl copy() {
KeymapImpl newKeymap = new KeymapImpl();
return copyTo(newKeymap);
return copyTo(new KeymapImpl());
}
@NotNull
public KeymapImpl copyTo(@NotNull KeymapImpl otherKeymap) {
public KeymapImpl copyTo(@NotNull final KeymapImpl otherKeymap) {
otherKeymap.myParent = myParent;
otherKeymap.myName = myName;
otherKeymap.myCanModify = canModify();
otherKeymap.cleanShortcutsCache();
for (Map.Entry<String, LinkedHashSet<Shortcut>> entry : myActionId2ListOfShortcuts.entrySet()) {
otherKeymap.myActionId2ListOfShortcuts.put(entry.getKey(), new LinkedHashSet<Shortcut>(entry.getValue()));
}
otherKeymap.myActionId2ListOfShortcuts.clear();
otherKeymap.myActionId2ListOfShortcuts.ensureCapacity(myActionId2ListOfShortcuts.size());
myActionId2ListOfShortcuts.forEachEntry(new TObjectObjectProcedure<String, OrderedSet<Shortcut>>() {
@Override
public boolean execute(String actionId, OrderedSet<Shortcut> shortcuts) {
otherKeymap.myActionId2ListOfShortcuts.put(actionId, new OrderedSet<Shortcut>(shortcuts));
return true;
}
});
return otherKeymap;
}
@@ -195,9 +201,9 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
private void addShortcutSilently(String actionId, Shortcut shortcut, final boolean checkParentShortcut) {
LinkedHashSet<Shortcut> list = myActionId2ListOfShortcuts.get(actionId);
OrderedSet<Shortcut> list = myActionId2ListOfShortcuts.get(actionId);
if (list == null) {
list = new LinkedHashSet<Shortcut>();
list = new OrderedSet<Shortcut>();
myActionId2ListOfShortcuts.put(actionId, list);
Shortcut[] boundShortcuts = getBoundShortcuts(actionId);
if (boundShortcuts != null) {
@@ -230,7 +236,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@Override
public void removeShortcut(String actionId, Shortcut toDelete) {
LinkedHashSet<Shortcut> list = myActionId2ListOfShortcuts.get(actionId);
OrderedSet<Shortcut> list = myActionId2ListOfShortcuts.get(actionId);
if (list != null) {
Iterator<Shortcut> it = list.iterator();
while (it.hasNext()) {
@@ -253,7 +259,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
if (inherited != null) {
boolean affected = false;
LinkedHashSet<Shortcut> newShortcuts = new LinkedHashSet<Shortcut>(inherited.length);
OrderedSet<Shortcut> newShortcuts = new OrderedSet<Shortcut>(inherited.length);
for (Shortcut eachInherited : inherited) {
if (toDelete.equals(eachInherited)) {
// skip this one
@@ -306,7 +312,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
private <T extends Shortcut>void addAction2ShortcutsMap(final String actionId, final Map<T, List<String>> strokesMap, final Class<T> shortcutClass) {
LinkedHashSet<Shortcut> listOfShortcuts = _getShortcuts(actionId);
OrderedSet<Shortcut> listOfShortcuts = _getShortcuts(actionId);
for (Shortcut shortcut : listOfShortcuts) {
if (!shortcutClass.isAssignableFrom(shortcut.getClass())) {
continue;
@@ -328,7 +334,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
private void addKeystrokesMap(final String actionId, final Map<KeyStroke, List<String>> strokesMap) {
LinkedHashSet<Shortcut> listOfShortcuts = _getShortcuts(actionId);
OrderedSet<Shortcut> listOfShortcuts = _getShortcuts(actionId);
for (Shortcut shortcut : listOfShortcuts) {
if (!(shortcut instanceof KeyboardShortcut)) {
continue;
@@ -347,14 +353,14 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
}
private LinkedHashSet<Shortcut> _getShortcuts(final String actionId) {
private OrderedSet<Shortcut> _getShortcuts(final String actionId) {
KeymapManagerEx keymapManager = getKeymapManager();
LinkedHashSet<Shortcut> listOfShortcuts = myActionId2ListOfShortcuts.get(actionId);
OrderedSet<Shortcut> listOfShortcuts = myActionId2ListOfShortcuts.get(actionId);
if (listOfShortcuts != null) {
return listOfShortcuts;
}
else {
listOfShortcuts = new LinkedHashSet<Shortcut>();
listOfShortcuts = new OrderedSet<Shortcut>();
}
final String actionBinding = keymapManager.getActionBinding(actionId);
@@ -519,7 +525,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@NotNull
@Override
public Shortcut[] getShortcuts(String actionId) {
LinkedHashSet<Shortcut> shortcuts = myActionId2ListOfShortcuts.get(actionId);
OrderedSet<Shortcut> shortcuts = myActionId2ListOfShortcuts.get(actionId);
if (shortcuts == null) {
Shortcut[] boundShortcuts = getBoundShortcuts(actionId);
@@ -539,7 +545,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@Nullable
private Shortcut[] getOwnShortcuts(String actionId) {
LinkedHashSet<Shortcut> own = myActionId2ListOfShortcuts.get(actionId);
OrderedSet<Shortcut> own = myActionId2ListOfShortcuts.get(actionId);
if (own == null) return null;
return own.isEmpty() ? ourEmptyShortcutsArray : own.toArray(new Shortcut[own.size()]);
}
@@ -685,7 +691,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
// Add read shortcuts
for (String id : id2shortcuts.keySet()) {
myActionId2ListOfShortcuts.put(id, new LinkedHashSet<Shortcut>(2)); // It's a trick! After that parent's shortcuts are not added to the keymap
myActionId2ListOfShortcuts.put(id, new OrderedSet<Shortcut>(2)); // It's a trick! After that parent's shortcuts are not added to the keymap
ArrayList<Shortcut> shortcuts = id2shortcuts.get(id);
for (Shortcut shortcut : shortcuts) {
addShortcutSilently(id, shortcut, false);
@@ -711,7 +717,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
for (String actionId : ownActionIds) {
Element actionElement = new Element(ACTION);
actionElement.setAttribute(ID_ATTRIBUTE, actionId);
// Save keyboad shortcuts
// Save keyboard shortcuts
Shortcut[] shortcuts = getShortcuts(actionId);
for (Shortcut shortcut : shortcuts) {
if (shortcut instanceof KeyboardShortcut) {
@@ -805,23 +811,23 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
// modifiers
int modifiers = shortcut.getModifiers();
if ((MouseEvent.SHIFT_DOWN_MASK & modifiers) > 0) {
if ((InputEvent.SHIFT_DOWN_MASK & modifiers) > 0) {
buffer.append(SHIFT);
buffer.append(' ');
}
if ((MouseEvent.CTRL_DOWN_MASK & modifiers) > 0) {
if ((InputEvent.CTRL_DOWN_MASK & modifiers) > 0) {
buffer.append(CONTROL);
buffer.append(' ');
}
if ((MouseEvent.META_DOWN_MASK & modifiers) > 0) {
if ((InputEvent.META_DOWN_MASK & modifiers) > 0) {
buffer.append(META);
buffer.append(' ');
}
if ((MouseEvent.ALT_DOWN_MASK & modifiers) > 0) {
if ((InputEvent.ALT_DOWN_MASK & modifiers) > 0) {
buffer.append(ALT);
buffer.append(' ');
}
if ((MouseEvent.ALT_GRAPH_DOWN_MASK & modifiers) > 0) {
if ((InputEvent.ALT_GRAPH_DOWN_MASK & modifiers) > 0) {
buffer.append(ALT_GRAPH);
buffer.append(' ');
}
@@ -849,7 +855,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
cleanShortcutsCache();
}
public boolean hasOwnActionId(String actionId) {
public boolean hasOwnActionId(@NotNull String actionId) {
return myActionId2ListOfShortcuts.containsKey(actionId);
}
@@ -86,7 +86,6 @@ public class ActionsTree {
if (rowBounds.intersects(clip)) {
Object node = getPathForRow(row).getLastPathComponent();
if (node instanceof DefaultMutableTreeNode) {
Object data = ((DefaultMutableTreeNode)node).getUserObject();
Rectangle fullRowRect = new Rectangle(visibleRect.x, rowBounds.y, visibleRect.width, rowBounds.height);
@@ -106,30 +105,20 @@ public class ActionsTree {
@Override
public void mouseMoved(MouseEvent e) {
String description = getDescription(e);
if (description != null) {
ActionMenu.showDescriptionInStatusBar(true, myTree, description);
}
else {
ActionMenu.showDescriptionInStatusBar(false, myTree, null);
}
ActionMenu.showDescriptionInStatusBar(description != null, myTree, description);
}
@Nullable
private String getDescription(@NotNull MouseEvent e) {
TreePath path = myTree.getPathForLocation(e.getX(), e.getY());
if (path == null) return null;
DefaultMutableTreeNode node = path == null ? null : (DefaultMutableTreeNode)path.getLastPathComponent();
Object userObject = node == null ? null : node.getUserObject();
if (!(userObject instanceof String)) {
return null;
}
DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
if (node == null) return null;
Object userObject = node.getUserObject();
if (!(userObject instanceof String)) return null;
String actionId = (String)userObject;
AnAction action = ActionManager.getInstance().getActionOrStub(actionId);
if (action == null) return null;
return action.getTemplatePresentation().getDescription();
AnAction action = ActionManager.getInstance().getActionOrStub((String)userObject);
return action == null ? null : action.getTemplatePresentation().getDescription();
}
});
@@ -140,6 +129,11 @@ public class ActionsTree {
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
}
// silently replace current map
void setKeymap(@NotNull Keymap keymap) {
myKeymap = keymap;
}
public JComponent getComponent() {
return myComponent;
}
@@ -171,7 +165,7 @@ public class ActionsTree {
return (QuickList)userObject;
}
public void reset(Keymap keymap, final QuickList[] allQuickLists) {
public void reset(@NotNull Keymap keymap, @NotNull QuickList[] allQuickLists) {
reset(keymap, allQuickLists, myFilter, null);
}
@@ -188,7 +182,7 @@ public class ActionsTree {
reset(myKeymap, currentQuickListIds, filter, null);
}
private void reset(final Keymap keymap, final QuickList[] allQuickLists, String filter, @Nullable KeyboardShortcut shortcut) {
private void reset(@NotNull Keymap keymap, @NotNull QuickList[] allQuickLists, String filter, @Nullable KeyboardShortcut shortcut) {
myKeymap = keymap;
final PathsKeeper pathsKeeper = new PathsKeeper();
@@ -198,11 +192,11 @@ public class ActionsTree {
ActionManager actionManager = ActionManager.getInstance();
Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(myComponent));
Group mainGroup = ActionsTreeUtil.createMainGroup(project, myKeymap, allQuickLists, filter, true,
ActionsTreeUtil.isActionFiltered(actionManager, myKeymap, shortcut, filter, true));
Group mainGroup = ActionsTreeUtil.createMainGroup(project, keymap, allQuickLists, filter, true,
ActionsTreeUtil.isActionFiltered(actionManager, keymap, shortcut, filter, true));
if ((filter != null && filter.length() > 0 || shortcut != null) && mainGroup.initIds().isEmpty()){
mainGroup = ActionsTreeUtil.createMainGroup(project, myKeymap, allQuickLists, filter, false,
ActionsTreeUtil.isActionFiltered(actionManager, myKeymap, shortcut, filter, false));
mainGroup = ActionsTreeUtil.createMainGroup(project, keymap, allQuickLists, filter, false,
ActionsTreeUtil.isActionFiltered(actionManager, keymap, shortcut, filter, false));
}
myRoot = ActionsTreeUtil.createNode(mainGroup);
myMainGroup = mainGroup;
@@ -251,14 +245,7 @@ public class ActionsTree {
if (userObject instanceof QuickList) {
userObject = ((QuickList)userObject).getActionId();
}
if (userObject instanceof String) {
Shortcut[] shortcuts = myKeymap.getShortcuts((String)userObject);
return KeymapUtil.getShortcutsText(shortcuts);
}
else {
return "";
}
return userObject instanceof String ? KeymapUtil.getShortcutsText(myKeymap.getShortcuts((String)userObject)) : "";
}
else {
return "???";
@@ -34,6 +34,7 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@@ -214,7 +215,8 @@ public class KeyboardShortcutDialog extends DialogWrapper {
myConflictInfoArea.setForeground(JBColor.RED);
if (loaded) {
myConflictInfoArea.setText(KeyMapBundle.message("assigned.to.info.message", buffer.toString()));
} else {
}
else {
myConflictInfoArea.setText("Assigned to " + buffer.toString() + " which is now not loaded but may be loaded later");
}
}
@@ -224,13 +226,10 @@ public class KeyboardShortcutDialog extends DialogWrapper {
mySecondStrokePanel.setEnabled(myEnableSecondKeystroke.isSelected());
}
@Nullable
public KeyboardShortcut getKeyboardShortcut() {
KeyStroke firstStroke = myFirstStrokePanel.getKeyStroke();
if (firstStroke == null) {
return null;
}
KeyStroke secondStroke = myEnableSecondKeystroke.isSelected() ? mySecondStrokePanel.getKeyStroke() : null;
return new KeyboardShortcut(firstStroke, secondStroke);
return firstStroke == null ? null : new KeyboardShortcut(firstStroke, myEnableSecondKeystroke.isSelected() ? mySecondStrokePanel.getKeyStroke() : null);
}
static String getTextByKeyStroke(KeyStroke keyStroke) {
@@ -119,8 +119,6 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
});
private Keymap mySelectedKeymap;
private JButton myCopyButton;
private JButton myDeleteButton;
private JButton myResetToDefault;
@@ -146,7 +144,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public void propertyChange(@NotNull final PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("ancestor") && evt.getNewValue() != null && evt.getOldValue() == null && myQuickListsModified) {
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
myQuickListsModified = false;
}
}
@@ -185,9 +183,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myEditor.getComboBox().addActionListener(new ActionListener() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
if (myEditor.getModel().getSelectedItem() != mySelectedKeymap) {
processCurrentKeymapChanged(myQuickLists);
}
currentKeymapChanged();
}
});
panel.add(createKeymapNamePanel(), new GridBagConstraints(3, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 10, 0, 0), 0, 0));
@@ -207,10 +203,13 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public void processCurrentKeymapChanged(@NotNull QuickList[] ids) {
myQuickLists = ids;
currentKeymapChanged();
}
private void currentKeymapChanged() {
myResetToDefault.setEnabled(false);
Keymap selectedKeymap = myEditor.getModel().getSelected();
mySelectedKeymap = selectedKeymap;
boolean editable = selectedKeymap != null && selectedKeymap.canModify();
myDeleteButton.setEnabled(editable);
@@ -228,16 +227,12 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
else {
myBaseKeymapLabel.setText(KeyMapBundle.message("based.on.keymap.label", parent.getPresentableName()));
if (selectedKeymap.canModify() && ((KeymapImpl)mySelectedKeymap).getOwnActionIds().length > 0) {
if (selectedKeymap.canModify() && ((KeymapImpl)selectedKeymap).getOwnActionIds().length > 0) {
myResetToDefault.setEnabled(true);
}
}
resetActionsTree(selectedKeymap);
}
private void resetActionsTree(@NotNull Keymap keymap) {
myActionsTree.reset(myEditor.getMutable(keymap), myQuickLists);
myActionsTree.reset(selectedKeymap, myQuickLists);
}
private JPanel createKeymapButtonsPanel() {
@@ -450,7 +445,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
private JPanel createFilteringPanel() {
//noinspection ConstantConditions
resetActionsTree(myEditor.getModel().getSelected());
myActionsTree.reset(myEditor.getModel().getSelected(), myQuickLists);
final JLabel firstLabel = new JLabel(KeyMapBundle.message("filter.first.stroke.input"));
final JCheckBox enable2Shortcut = new JCheckBox(KeyMapBundle.message("filter.second.stroke.input"));
@@ -482,12 +477,11 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
});
IJSwingUtilities.adjustComponentsOnMac(firstLabel, firstShortcut);
//noinspection deprecation
JPanel filterComponent = FormBuilder.createFormBuilder()
.addLabeledComponent(firstLabel, firstShortcut, true)
.addComponent(enable2Shortcut)
.setVerticalGap(0)
.setIndent(5)
.setHorizontalGap(5)
.addComponent(secondShortcut)
.getPanel();
@@ -525,31 +519,21 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myActionsTree.filter(option, myQuickLists);
}
private void addKeyboardShortcut(Shortcut shortcut) {
String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) {
return;
}
if (!createKeymapCopyIfNeeded()) return;
private void addKeyboardShortcut(@NotNull String actionId, @Nullable Shortcut shortcut) {
Keymap keymap = createKeymapCopyIfNeeded();
KeyboardShortcutDialog dialog = new KeyboardShortcutDialog(this, actionId, myQuickLists);
KeyboardShortcut selectedKeyboardShortcut = shortcut instanceof KeyboardShortcut ? (KeyboardShortcut)shortcut : null;
dialog.setData(mySelectedKeymap, selectedKeyboardShortcut);
dialog.setData(keymap, shortcut instanceof KeyboardShortcut ? (KeyboardShortcut)shortcut : null);
if (!dialog.showAndGet()) {
return;
}
KeyboardShortcut keyboardShortcut = dialog.getKeyboardShortcut();
if (keyboardShortcut == null) {
return;
}
Map<String, ArrayList<KeyboardShortcut>> conflicts = mySelectedKeymap.getConflicts(actionId, keyboardShortcut);
Map<String, ArrayList<KeyboardShortcut>> conflicts = keymap.getConflicts(actionId, keyboardShortcut);
if (!conflicts.isEmpty()) {
int result = Messages.showYesNoCancelDialog(
this,
@@ -563,7 +547,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
if (result == Messages.YES) {
for (String id : conflicts.keySet()) {
for (KeyboardShortcut s : conflicts.get(id)) {
mySelectedKeymap.removeShortcut(id, s);
keymap.removeShortcut(id, s);
}
}
}
@@ -573,20 +557,20 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
// if shortcut is already registered to this action, just select it in the list
Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId);
Shortcut[] shortcuts = keymap.getShortcuts(actionId);
for (Shortcut s : shortcuts) {
if (s.equals(keyboardShortcut)) {
return;
}
}
mySelectedKeymap.addShortcut(actionId, keyboardShortcut);
keymap.addShortcut(actionId, keyboardShortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
mySelectedKeymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), keyboardShortcut);
keymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), keyboardShortcut);
}
repaintLists();
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
private void addMouseShortcut(Shortcut shortcut, ShortcutRestrictions restrictions) {
@@ -595,14 +579,14 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
return;
}
if (!createKeymapCopyIfNeeded()) return;
Keymap keymap = createKeymapCopyIfNeeded();
MouseShortcut mouseShortcut = shortcut instanceof MouseShortcut ? (MouseShortcut)shortcut : null;
MouseShortcutDialog dialog = new MouseShortcutDialog(
this,
mouseShortcut,
mySelectedKeymap,
keymap,
actionId,
myActionsTree.getMainGroup(),
restrictions
@@ -617,7 +601,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
return;
}
String[] actionIds = mySelectedKeymap.getActionIds(mouseShortcut);
String[] actionIds = keymap.getActionIds(mouseShortcut);
if (actionIds.length > 1 || (actionIds.length == 1 && !actionId.equals(actionIds[0]))) {
int result = Messages.showYesNoCancelDialog(
this,
@@ -630,7 +614,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
if (result == Messages.YES) {
for (String id : actionIds) {
mySelectedKeymap.removeShortcut(id, mouseShortcut);
keymap.removeShortcut(id, mouseShortcut);
}
}
else if (result != Messages.NO) {
@@ -640,50 +624,51 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
// if shortcut is already registered to this action, just select it in the list
Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId);
Shortcut[] shortcuts = keymap.getShortcuts(actionId);
for (Shortcut shortcut1 : shortcuts) {
if (shortcut1.equals(mouseShortcut)) {
return;
}
}
mySelectedKeymap.addShortcut(actionId, mouseShortcut);
keymap.addShortcut(actionId, mouseShortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
mySelectedKeymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), mouseShortcut);
keymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), mouseShortcut);
}
repaintLists();
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
private void repaintLists() {
myActionsTree.getComponent().repaint();
}
private boolean createKeymapCopyIfNeeded() {
if (mySelectedKeymap.canModify()) return true;
final Keymap selectedKeymap = myEditor.getModel().getSelected();
if (selectedKeymap == null) {
return false;
@NotNull
private Keymap createKeymapCopyIfNeeded() {
Keymap keymap = myEditor.getModel().getSelected();
assert keymap != null;
if (keymap.canModify()) {
Keymap mutable = myEditor.getMutable(keymap);
myActionsTree.setKeymap(mutable);
return mutable;
}
KeymapImpl newKeymap = ((KeymapImpl)selectedKeymap).deriveKeymap();
String newKeymapName = KeyMapBundle.message("new.keymap.name", selectedKeymap.getPresentableName());
String newKeymapName = KeyMapBundle.message("new.keymap.name", keymap.getPresentableName());
if (!tryNewKeymapName(newKeymapName)) {
for (int i = 0; ; i++) {
newKeymapName = KeyMapBundle.message("new.indexed.keymap.name", selectedKeymap.getPresentableName(), i);
newKeymapName = KeyMapBundle.message("new.indexed.keymap.name", keymap.getPresentableName(), i);
if (tryNewKeymapName(newKeymapName)) {
break;
}
}
}
KeymapImpl newKeymap = ((KeymapImpl)keymap).deriveKeymap();
newKeymap.setName(newKeymapName);
newKeymap.setCanModify(true);
final int indexOf = myEditor.getModel().getElementIndex(selectedKeymap);
int indexOf = myEditor.getModel().getElementIndex(keymap);
if (indexOf >= 0) {
myEditor.getModel().add(indexOf + 1, newKeymap);
}
@@ -692,28 +677,8 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
myEditor.getModel().setSelectedItem(newKeymap);
processCurrentKeymapChanged(myQuickLists);
return true;
}
private void removeShortcut(Shortcut shortcut) {
String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) {
return;
}
if (!createKeymapCopyIfNeeded()) return;
if (shortcut == null) return;
mySelectedKeymap.removeShortcut(actionId, shortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
mySelectedKeymap.removeShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), shortcut);
}
repaintLists();
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
return newKeymap;
}
private void copyKeymap() {
@@ -738,7 +703,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myEditor.getModel().add(newKeymap);
myEditor.getModel().setSelectedItem(newKeymap);
myEditor.getComboBox().getEditor().selectAll();
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
private boolean tryNewKeymapName(String name) {
@@ -759,7 +724,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
myEditor.getModel().remove(keymap);
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
private void resetKeymap() {
@@ -768,7 +733,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
return;
}
((KeymapImpl)keymap).clearOwnActionsIds();
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
@Override
@@ -784,11 +749,11 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myNonEnglishKeyboardSupportOption.setSelected(KeyboardSettingsExternalizable.getInstance().isNonEnglishKeyboardSupportEnabled());
}
mySelectedKeymap = null;
Keymap selectedKeymap = null;
List<Keymap> list = getManagerKeymaps();
for (Keymap keymap : list) {
if (mySelectedKeymap == null && keymap == KeymapManagerEx.getInstanceEx().getActiveKeymap()) {
mySelectedKeymap = keymap;
if (selectedKeymap == null && keymap == KeymapManagerEx.getInstanceEx().getActiveKeymap()) {
selectedKeymap = keymap;
}
}
myEditor.reset(list);
@@ -797,23 +762,23 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
KeymapImpl keymap = new KeymapImpl();
keymap.setName(KeyMapBundle.message("keymap.no.name"));
myEditor.getModel().add(keymap);
mySelectedKeymap = keymap;
selectedKeymap = keymap;
}
myEditor.getModel().setSelectedItem(mySelectedKeymap);
myEditor.getModel().setSelectedItem(selectedKeymap);
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
@Override
public void apply() throws ConfigurationException {
myEditor.ensureNonEmptyNames("Quick list should have non empty name");
myEditor.ensureNonEmptyNames(KeyMapBundle.message("configuration.all.keymaps.should.have.non.empty.names.error.message"));
ensureUniqueKeymapNames();
KeymapManagerImpl keymapManager = (KeymapManagerImpl)KeymapManager.getInstance();
// we must specify the same filter, which was used to get original items
keymapManager.setKeymaps(myEditor.apply(), KEYMAP_FILTER);
keymapManager.setActiveKeymap(mySelectedKeymap);
keymapManager.setActiveKeymap(myEditor.getModel().getSelected());
ActionToolbarImpl.updateAllToolbarsImmediately();
}
@@ -828,7 +793,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public boolean isModified() {
return !Comparing.equal(mySelectedKeymap, KeymapManager.getInstance().getActiveKeymap()) || myEditor.isModified();
return !Comparing.equal(myEditor.getModel().getSelected(), KeymapManager.getInstance().getActiveKeymap()) || myEditor.isModified();
}
@NotNull
@@ -873,34 +838,59 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Nullable
public Shortcut[] getCurrentShortcuts(@NotNull String actionId) {
return mySelectedKeymap == null ? null : mySelectedKeymap.getShortcuts(actionId);
Keymap keymap = myEditor.getModel().getSelected();
return keymap == null ? null : keymap.getShortcuts(actionId);
}
private void editSelection(InputEvent e) {
final String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) return;
String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) {
return;
}
DefaultActionGroup group = createEditActionGroup(actionId);
if (e instanceof MouseEvent && ((MouseEvent)e).isPopupTrigger()) {
ActionManager.getInstance()
.createActionPopupMenu(ActionPlaces.UNKNOWN, group)
.getComponent()
.show(e.getComponent(), ((MouseEvent)e).getX(), ((MouseEvent)e).getY());
}
else {
DataContext dataContext = DataManager.getInstance().getDataContext(this);
ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup("Edit Shortcuts",
group,
dataContext,
JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,
true);
if (e instanceof MouseEvent) {
popup.show(new RelativePoint((MouseEvent)e));
}
else {
popup.showInBestPositionFor(dataContext);
}
}
}
@NotNull
private DefaultActionGroup createEditActionGroup(@NotNull final String actionId) {
DefaultActionGroup group = new DefaultActionGroup();
final Shortcut[] shortcuts = getCurrentShortcuts(actionId);
final Set<String> abbreviations = AbbreviationManager.getInstance().getAbbreviations(actionId);
final ShortcutRestrictions restrictions = ActionShortcutRestrictions.getInstance().getForActionId(actionId);
if (restrictions.allowKeyboardShortcut) {
group.add(new DumbAwareAction("Add Keyboard Shortcut") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Shortcut firstKeyboard = null;
assert shortcuts != null;
for (Shortcut shortcut : shortcuts) {
Shortcut firstShortcut = null;
Keymap keymap = myEditor.getModel().getSelected();
assert keymap != null;
for (Shortcut shortcut : keymap.getShortcuts(actionId)) {
if (shortcut instanceof KeyboardShortcut) {
firstKeyboard = shortcut;
firstShortcut = shortcut;
break;
}
}
addKeyboardShortcut(firstKeyboard);
addKeyboardShortcut(actionId, firstShortcut);
}
});
}
@@ -910,8 +900,9 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Shortcut firstMouse = null;
assert shortcuts != null;
for (Shortcut shortcut : shortcuts) {
Keymap keymap = myEditor.getModel().getSelected();
assert keymap != null;
for (Shortcut shortcut : keymap.getShortcuts(actionId)) {
if (shortcut instanceof MouseShortcut) {
firstMouse = shortcut;
break;
@@ -926,10 +917,9 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
group.add(new DumbAwareAction("Add Abbreviation") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
final String abbr = Messages.showInputDialog("Enter new abbreviation:", "Abbreviation", null);
String abbr = Messages.showInputDialog("Enter new abbreviation:", "Abbreviation", null);
if (abbr != null) {
String actionId = myActionsTree.getSelectedActionId();
AbbreviationManager.getInstance().register(abbr, actionId);
AbbreviationManager.getInstance().register(abbr, myActionsTree.getSelectedActionId());
repaintLists();
}
}
@@ -943,29 +933,32 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
group.addSeparator();
assert shortcuts != null;
for (final Shortcut shortcut : shortcuts) {
Keymap keymap = myEditor.getModel().getSelected();
assert keymap != null;
for (final Shortcut shortcut : keymap.getShortcuts(actionId)) {
group.add(new DumbAwareAction("Remove " + KeymapUtil.getShortcutText(shortcut)) {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
removeShortcut(shortcut);
Keymap keymap = createKeymapCopyIfNeeded();
keymap.removeShortcut(actionId, shortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
keymap.removeShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), shortcut);
}
repaintLists();
currentKeymapChanged();
}
});
}
if (Registry.is("actionSystem.enableAbbreviations")) {
for (final String abbreviation : abbreviations) {
for (final String abbreviation : AbbreviationManager.getInstance().getAbbreviations(actionId)) {
group.addAction(new DumbAwareAction("Remove Abbreviation '" + abbreviation + "'") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
AbbreviationManager.getInstance().remove(abbreviation, actionId);
repaintLists();
}
@Override
public void update(@NotNull AnActionEvent e) {
super.update(e);
}
});
}
}
@@ -973,37 +966,17 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
group.add(new DumbAwareAction("Reset Shortcuts") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
((KeymapImpl)mySelectedKeymap).clearOwnActionsId(actionId);
processCurrentKeymapChanged(myQuickLists);
((KeymapImpl)createKeymapCopyIfNeeded()).clearOwnActionsId(actionId);
currentKeymapChanged();
repaintLists();
}
@Override
public void update(@NotNull AnActionEvent e) {
e.getPresentation().setVisible(mySelectedKeymap.canModify() && ((KeymapImpl)mySelectedKeymap).hasOwnActionId(actionId));
super.update(e);
e.getPresentation().setVisible(((KeymapImpl)myEditor.getModel().getSelected()).hasOwnActionId(actionId));
}
});
if (e instanceof MouseEvent && ((MouseEvent)e).isPopupTrigger()) {
final ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group);
popupMenu.getComponent().show(e.getComponent(), ((MouseEvent)e).getX(), ((MouseEvent)e).getY());
}
else {
final DataContext dataContext = DataManager.getInstance().getDataContext(this);
final ListPopup popup = JBPopupFactory.getInstance()
.createActionGroupPopup("Edit Shortcuts",
group,
dataContext,
JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,
true);
if (e instanceof MouseEvent) {
popup.show(new RelativePoint((MouseEvent)e));
}
else {
popup.showInBestPositionFor(dataContext);
}
}
return group;
}
}
@@ -32,7 +32,7 @@
</constraints>
<properties/>
</component>
<component id="1d65e" class="javax.swing.JTextField" binding="myDisplayName">
<component id="1d65e" class="javax.swing.JTextField" binding="myName">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
@@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ex.QuickList;
import com.intellij.openapi.actionSystem.ex.QuickListsManager;
import com.intellij.openapi.keymap.KeyMapBundle;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.ui.*;
@@ -36,7 +37,7 @@ class QuickListPanel {
private final CollectionListModel<Object> actionsModel;
private JPanel myPanel;
private final JBList myActionsList;
JTextField myDisplayName;
JTextField myName;
private JTextField myDescription;
private JPanel myListPanel;
QuickList item;
@@ -97,10 +98,10 @@ class QuickListPanel {
return;
}
item.setName(myDisplayName.getText().trim());
item.setName(myName.getText().trim());
item.setDescription(myDescription.getText().trim());
ListModel model = getActionsList().getModel();
ListModel model = myActionsList.getModel();
int size = model.getSize();
String[] ids;
if (size == 0) {
@@ -124,7 +125,8 @@ class QuickListPanel {
return;
}
myDisplayName.setText(this.item.getName());
myName.setText(item.getName());
myName.setEnabled(QuickListsManager.getInstance().getSchemeManager().isMetadataEditable(item));
myDescription.setText(item.getDescription());
actionsModel.removeAll();
@@ -140,18 +142,6 @@ class QuickListPanel {
}
}
public JList getActionsList() {
return myActionsList;
}
public String getDescription() {
return myDescription.getText();
}
public String getDisplayName() {
return myDisplayName.getText();
}
private void includeActionId(@NotNull String id) {
if (QuickList.SEPARATOR_ID.equals(id) || actionsModel.getElementIndex(id) == -1) {
actionsModel.add(id);
@@ -30,7 +30,7 @@ public class QuickListsPanel extends ConfigurableBase<QuickListsUi, List<QuickLi
@NotNull
@Override
protected List<QuickList> getSettings() {
return QuickListsManager.getInstance().getQuickLists();
return QuickListsManager.getInstance().getSchemeManager().getAllSchemes();
}
@Override
@@ -62,7 +62,13 @@ class QuickListsUi implements ConfigurableUi<List<QuickList>> {
public String getName(@NotNull QuickList item) {
return item.getName();
}
@Override
public boolean isRemovable(@NotNull QuickList item) {
return QuickListsManager.getInstance().getSchemeManager().isMetadataEditable(item);
}
};
private final ListModelEditor<QuickList> editor = new ListModelEditor<QuickList>(itemEditor);
private JComponent component;
@@ -74,6 +80,8 @@ class QuickListsUi implements ConfigurableUi<List<QuickList>> {
final CardLayout cardLayout = new CardLayout();
// doesn't make any sense (and in any case scheme manager cannot preserve order)
editor.disableUpDownActions();
editor.getList().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
@@ -90,12 +98,12 @@ class QuickListsUi implements ConfigurableUi<List<QuickList>> {
});
itemPanel = new QuickListPanel(editor.getModel());
itemPanel.myDisplayName.getDocument().addDocumentListener(new DocumentAdapter() {
itemPanel.myName.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
QuickList item = itemPanel.item;
if (item != null) {
String name = itemPanel.myDisplayName.getText();
String name = itemPanel.myName.getText();
boolean changed = !item.getName().equals(name);
item.setName(name);
if (changed) {
@@ -0,0 +1,831 @@
/*
* 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.options
import com.intellij.openapi.application.AccessToken
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.application.ex.DecodeDefaultsUtil
import com.intellij.openapi.application.writeAction
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.components.impl.stores.DirectoryBasedStorage
import com.intellij.openapi.components.impl.stores.DirectoryStorageData
import com.intellij.openapi.components.impl.stores.StorageUtil
import com.intellij.openapi.components.impl.stores.StreamProvider
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.AbstractExtensionPointBean
import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.Condition
import com.intellij.openapi.util.JDOMUtil
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtilRt
import com.intellij.openapi.vfs.*
import com.intellij.openapi.vfs.tracker.VirtualFileTracker
import com.intellij.util.PathUtil
import com.intellij.util.PathUtilRt
import com.intellij.util.SmartList
import com.intellij.util.ThrowableConvertor
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.io.URLUtil
import com.intellij.util.text.UniqueNameGenerator
import gnu.trove.THashMap
import gnu.trove.THashSet
import gnu.trove.TObjectObjectProcedure
import gnu.trove.TObjectProcedure
import org.jdom.Document
import org.jdom.Element
import java.io.File
import java.io.InputStream
import java.util.ArrayList
import java.util.Collections
val LOG = Logger.getInstance(javaClass<SchemesManagerFactoryImpl>())
public class SchemeManagerImpl<T : Scheme, E : ExternalizableScheme>(private val fileSpec: String,
private val processor: SchemeProcessor<E>,
private val roamingType: RoamingType,
private val provider: StreamProvider?,
private val ioDirectory: File) : SchemesManager<T, E>(), SafeWriteRequestor {
private val schemes = ArrayList<T>()
private val readOnlyExternalizableSchemes = THashMap<String, E>()
private var currentScheme: T? = null
private var currentSchemeName: String? = null
private var directory: VirtualFile? = null
private val schemeExtension: String
private val updateExtension: Boolean
private val filesToDelete = THashSet<String>()
// scheme could be changed - so, hashcode will be changed - we must use identity hashing strategy
private val schemeToInfo = THashMap<E, ExternalInfo>(ContainerUtil.identityStrategy())
init {
if (processor is SchemeExtensionProvider) {
schemeExtension = processor.getSchemeExtension()
updateExtension = processor.isUpgradeNeeded()
}
else {
schemeExtension = DirectoryStorageData.DEFAULT_EXT
updateExtension = false
}
val application = ApplicationManager.getApplication()
val virtualFileTracker = ServiceManager.getService(javaClass<VirtualFileTracker>())
if (virtualFileTracker != null) {
val baseDirPath = ioDirectory.getAbsolutePath().replace(File.separatorChar, '/')
virtualFileTracker.addTracker(LocalFileSystem.PROTOCOL_PREFIX + baseDirPath, object : VirtualFileAdapter() {
override fun contentsChanged(event: VirtualFileEvent) {
if (event.getRequestor() != null || !isMy(event)) {
return
}
val scheme = findExternalizableSchemeByFileName(event.getFile().getName())
var oldCurrentScheme: T? = null
if (scheme != null) {
oldCurrentScheme = getCurrentScheme()
@suppress("UNCHECKED_CAST")
removeScheme(scheme as T)
processor.onSchemeDeleted(scheme)
}
val readScheme = readSchemeFromFile(event.getFile(), false)
if (readScheme != null) {
processor.initScheme(readScheme)
processor.onSchemeAdded(readScheme)
var newCurrentScheme = getCurrentScheme()
if (oldCurrentScheme != null && newCurrentScheme == null) {
setCurrentSchemeName(readScheme.getName())
newCurrentScheme = getCurrentScheme()
}
if (oldCurrentScheme !== newCurrentScheme) {
processor.onCurrentSchemeChanged(oldCurrentScheme)
}
}
}
override fun fileCreated(event: VirtualFileEvent) {
if (event.getRequestor() == null) {
if (event.getFile().isDirectory()) {
val dir = getDirectory()
if (event.getFile() == dir) {
for (file in dir!!.getChildren()) {
if (isMy(file)) {
schemeCreatedExternally(file)
}
}
}
}
else if (isMy(event)) {
schemeCreatedExternally(event.getFile())
}
}
}
private fun schemeCreatedExternally(file: VirtualFile) {
val readScheme = readSchemeFromFile(file, false)
if (readScheme != null) {
processor.initScheme(readScheme)
processor.onSchemeAdded(readScheme)
}
}
override fun fileDeleted(event: VirtualFileEvent) {
if (event.getRequestor() == null) {
if (event.getFile().isDirectory()) {
val dir = directory
if (event.getFile() == dir) {
directory = null
for (file in dir!!.getChildren()) {
if (isMy(file)) {
schemeDeletedExternally(file)
}
}
}
}
else if (isMy(event)) {
schemeDeletedExternally(event.getFile())
}
}
}
private fun schemeDeletedExternally(file: VirtualFile) {
val scheme = findExternalizableSchemeByFileName(file.getName())
var oldCurrentScheme: T? = null
if (scheme != null) {
oldCurrentScheme = getCurrentScheme()
@suppress("UNCHECKED_CAST")
removeScheme(scheme as T)
processor.onSchemeDeleted(scheme)
}
var newCurrentScheme = getCurrentScheme()
if (oldCurrentScheme != null && newCurrentScheme == null) {
if (!schemes.isEmpty()) {
setCurrentSchemeName(schemes.get(0).getName())
newCurrentScheme = getCurrentScheme()
}
}
if (oldCurrentScheme !== newCurrentScheme) {
processor.onCurrentSchemeChanged(oldCurrentScheme)
}
}
}, false, application)
}
}
override fun loadBundledScheme(resourceName: String, requestor: Any, convertor: ThrowableConvertor<Element, T, Throwable>) {
try {
val url = if (requestor is AbstractExtensionPointBean)
requestor.getLoaderForClass().getResource(resourceName)
else
DecodeDefaultsUtil.getDefaults(requestor, resourceName)
if (url == null) {
LOG.error("Cannot read scheme from $resourceName")
return
}
val element = JDOMUtil.load(URLUtil.openStream(url))
val scheme = convertor.convert(element)
if (scheme is ExternalizableScheme) {
val fileName = PathUtilRt.getFileName(url.getPath())
val extension = getFileExtension(fileName, true)
val info = ExternalInfo(fileName.substring(0, fileName.length() - extension.length()), extension)
info.hash = JDOMUtil.getTreeHash(element, true)
info.schemeName = scheme.getName()
@suppress("UNCHECKED_CAST")
val oldInfo = schemeToInfo.put(scheme as E, info)
LOG.assertTrue(oldInfo == null)
val oldScheme = readOnlyExternalizableSchemes.put(scheme.getName(), scheme)
if (oldScheme != null) {
LOG.warn("Duplicated scheme ${scheme.getName()} - old: $oldScheme, new $scheme")
}
}
schemes.add(scheme)
}
catch (e: Throwable) {
LOG.error("Cannot read scheme from $resourceName", e)
}
}
private fun getFileExtension(fileName: CharSequence, allowAny: Boolean): String {
return if (StringUtilRt.endsWithIgnoreCase(fileName, schemeExtension)) {
schemeExtension
}
else if (StringUtilRt.endsWithIgnoreCase(fileName, DirectoryStorageData.DEFAULT_EXT)) {
DirectoryStorageData.DEFAULT_EXT
}
else if (allowAny) {
PathUtil.getFileExtension(fileName.toString())!!
}
else {
throw IllegalStateException("Scheme file extension $fileName is unknown, must be filtered out")
}
}
private fun isMy(event: VirtualFileEvent) = isMy(event.getFile())
private fun isMy(file: VirtualFile) = StringUtilRt.endsWithIgnoreCase(file.getNameSequence(), schemeExtension)
override fun loadSchemes(): Collection<E> {
val newSchemesOffset = schemes.size()
if (provider != null && provider.isEnabled()) {
provider.processChildren(fileSpec, roamingType, { canRead(it) }) { name, input, readOnly ->
val scheme = loadScheme(name, input, true)
if (readOnly && scheme != null) {
readOnlyExternalizableSchemes.put(scheme.name, scheme)
}
true
}
}
else {
val dir = getDirectory()
val files = dir?.getChildren()
if (files != null) {
for (file in files) {
readSchemeFromFile(file, true)
}
}
}
val list = SmartList<E>()
for (i in newSchemesOffset..schemes.size() - 1) {
@suppress("UNCHECKED_CAST")
val scheme = schemes[i] as E
processor.initScheme(scheme)
list.add(scheme)
}
return list
}
public fun reload() {
// we must not remove non-persistent (e.g. predefined) schemes, because we cannot load it (obviously)
for (i in schemes.indices.reversed()) {
val scheme = schemes.get(i)
@suppress("UNCHECKED_CAST")
if (scheme is ExternalizableScheme && getState(scheme as E) != BaseSchemeProcessor.State.NON_PERSISTENT) {
schemes.remove(i)
if (scheme === currentScheme) {
currentScheme = null
}
}
}
retainExternalInfo(schemes)
loadSchemes()
}
private fun findExternalizableSchemeByFileName(fileName: String): E? {
for (scheme in schemes) {
@suppress("UNCHECKED_CAST")
if (scheme is ExternalizableScheme && fileName == "${getFileName(scheme)}$schemeExtension") {
return scheme as E
}
}
return null
}
private fun isOverwriteOnLoad(existingScheme: E): Boolean {
if (readOnlyExternalizableSchemes.get(existingScheme.getName()) === existingScheme) {
// so, bundled scheme is shadowed
return true
}
val info = schemeToInfo.get(existingScheme)
// scheme from file with old extension, so, we must ignore it
return info != null && schemeExtension != info.fileExtension
}
private fun loadScheme(fileName: CharSequence, input: InputStream, duringLoad: Boolean): E? {
try {
val element = JDOMUtil.load(input)
@suppress("DEPRECATED_SYMBOL_WITH_MESSAGE", "UNCHECKED_CAST")
val scheme = (if (processor is BaseSchemeProcessor<*>) {
processor.readScheme(element, duringLoad) as E?
}
else {
processor.readScheme(Document(element.detach() as Element))
}) ?: return null
val extension = getFileExtension(fileName, false)
val fileNameWithoutExtension = fileName.subSequence(0, fileName.length() - extension.length()).toString()
if (duringLoad) {
if (filesToDelete.isNotEmpty() && filesToDelete.contains(fileName.toString())) {
LOG.warn("Scheme file $fileName is not loaded because marked to delete")
return null
}
val existingScheme = findSchemeByName(scheme.getName())
if (existingScheme != null) {
@suppress("UNCHECKED_CAST")
if (existingScheme is ExternalizableScheme && isOverwriteOnLoad(existingScheme as E)) {
removeScheme(existingScheme)
}
else {
// We don't load scheme with duplicated name - if we generate unique name for it, it will be saved then with new name.
// It is not what all can expect. Such situation in most cases indicates error on previous level, so, we just warn about it.
LOG.warn("Scheme file $fileName is not loaded because defines duplicated name ${scheme.getName()}")
return null
}
}
}
var info: ExternalInfo? = schemeToInfo.get(scheme)
if (info == null) {
info = ExternalInfo(fileNameWithoutExtension, extension)
schemeToInfo.put(scheme, info)
}
else {
info.setFileNameWithoutExtension(fileNameWithoutExtension, extension)
}
info.hash = JDOMUtil.getTreeHash(element, true)
info.schemeName = scheme.getName()
@suppress("UNCHECKED_CAST")
if (duringLoad) {
schemes.add(scheme as T)
}
else {
addScheme(scheme as T)
}
return scheme
}
catch (e: Exception) {
LOG.error("Cannot read scheme $fileName", e)
return null
}
}
private fun getFileName(scheme: ExternalizableScheme) = schemeToInfo.get(scheme)?.fileNameWithoutExtension
private fun canRead(name: CharSequence): Boolean {
return updateExtension && StringUtilRt.endsWithIgnoreCase(name, DirectoryStorageData.DEFAULT_EXT) || StringUtilRt.endsWithIgnoreCase(name, schemeExtension)
}
private fun readSchemeFromFile(file: VirtualFile, duringLoad: Boolean): E? {
val fileName = file.getNameSequence()
if (file.isDirectory() || !canRead(fileName)) {
return null
}
try {
return loadScheme(fileName, file.getInputStream(), duringLoad)
}
catch (e: Throwable) {
LOG.error("Cannot read scheme $fileName", e)
return null
}
}
fun save(errors: MutableList<Throwable>) {
var hasSchemes = false
val nameGenerator = UniqueNameGenerator()
val schemesToSave = SmartList<E>()
for (scheme in schemes) {
@suppress("UNCHECKED_CAST")
if (scheme is ExternalizableScheme) {
val state = getState(scheme as E)
if (state === BaseSchemeProcessor.State.NON_PERSISTENT) {
continue
}
hasSchemes = true
if (state !== BaseSchemeProcessor.State.UNCHANGED) {
schemesToSave.add(scheme)
}
val fileName = getFileName(scheme)
if (fileName != null && !isRenamed(scheme)) {
nameGenerator.addExistingName(fileName)
}
}
}
for (scheme in schemesToSave) {
errors.catch {
saveScheme(scheme, nameGenerator)
}
}
val dir = getDirectory()
deleteFiles(dir, errors)
if (!hasSchemes && dir != null) {
removeDirectoryIfEmpty(dir, errors)
}
}
private fun removeDirectoryIfEmpty(dir: VirtualFile, errors: MutableList<Throwable>) {
for (file in dir.getChildren()) {
if (!file.`is`(VFileProperty.HIDDEN)) {
LOG.info("Directory " + dir.getNameSequence() + " is not deleted: at least one file " + file.getNameSequence() + " exists")
return
}
}
LOG.info("Remove schemes directory " + dir.getNameSequence())
directory = null
val token = WriteAction.start()
try {
dir.delete(this)
}
catch (e: Throwable) {
errors.add(e)
}
finally {
token.finish()
}
}
private fun getState(scheme: E): BaseSchemeProcessor.State {
return if (processor is BaseSchemeProcessor<*>) {
(processor as BaseSchemeProcessor<E>).getState(scheme)
}
else {
@suppress("DEPRECATED_SYMBOL_WITH_MESSAGE")
if (processor.shouldBeSaved(scheme)) BaseSchemeProcessor.State.POSSIBLY_CHANGED else BaseSchemeProcessor.State.NON_PERSISTENT
}
}
private fun saveScheme(scheme: E, nameGenerator: UniqueNameGenerator) {
var externalInfo: ExternalInfo? = schemeToInfo.get(scheme)
val currentFileNameWithoutExtension = if (externalInfo == null) null else externalInfo.fileNameWithoutExtension
val parent = processor.writeScheme(scheme)
val element = if (parent == null || parent is Element) parent as Element? else (parent as Document).detachRootElement()
if (JDOMUtil.isEmpty(element)) {
externalInfo?.scheduleDelete()
return
}
var fileNameWithoutExtension = currentFileNameWithoutExtension
if (fileNameWithoutExtension == null || isRenamed(scheme)) {
fileNameWithoutExtension = nameGenerator.generateUniqueName(FileUtil.sanitizeName(scheme.getName()))
}
val newHash = JDOMUtil.getTreeHash(element!!, true)
if (externalInfo != null && currentFileNameWithoutExtension === fileNameWithoutExtension && newHash == externalInfo.hash) {
return
}
// save only if scheme differs from bundled
val bundledScheme = readOnlyExternalizableSchemes.get(scheme.getName())
if (bundledScheme != null && schemeToInfo.get(bundledScheme)!!.hash == newHash) {
externalInfo?.scheduleDelete()
return
}
val fileName = fileNameWithoutExtension!! + schemeExtension
// file will be overwritten, so, we don't need to delete it
filesToDelete.remove(fileName)
// stream provider always use LF separator
val byteOut = StorageUtil.writeToBytes(element, "\n")
var providerPath: String?
if (provider != null && provider.isEnabled()) {
providerPath = fileSpec + '/' + fileName
if (!provider.isApplicable(providerPath, roamingType)) {
providerPath = null
}
}
else {
providerPath = null
}
// if another new scheme uses old name of this scheme, so, we must not delete it (as part of rename operation)
val renamed = externalInfo != null && fileNameWithoutExtension !== currentFileNameWithoutExtension && nameGenerator.value(currentFileNameWithoutExtension)
if (providerPath == null) {
var file: VirtualFile? = null
var dir = getDirectory()
if (dir == null || !dir.isValid()) {
dir = DirectoryBasedStorage.createDir(ioDirectory, this)
directory = dir!!
}
if (renamed) {
file = dir.findChild(externalInfo!!.fileName)
if (file != null) {
writeAction {
file!!.rename(this, fileName)
}
}
}
if (file == null) {
file = DirectoryBasedStorage.getFile(fileName, dir, this)
}
writeAction {
file!!.getOutputStream(this).use {
byteOut.writeTo(it)
}
}
}
else {
if (renamed) {
externalInfo!!.scheduleDelete()
}
provider!!.saveContent(providerPath, byteOut.getInternalBuffer(), byteOut.size(), roamingType)
}
if (externalInfo == null) {
externalInfo = ExternalInfo(fileNameWithoutExtension, schemeExtension)
schemeToInfo.put(scheme, externalInfo)
}
else {
externalInfo.setFileNameWithoutExtension(fileNameWithoutExtension, schemeExtension)
}
externalInfo.hash = newHash
externalInfo.schemeName = scheme.getName()
}
private fun ExternalInfo.scheduleDelete() {
filesToDelete.add(fileName)
}
private fun isRenamed(scheme: ExternalizableScheme): Boolean {
val info = schemeToInfo.get(scheme)
return info != null && scheme.getName() != info.schemeName
}
private fun deleteFiles(dir: VirtualFile?, errors: MutableList<Throwable>) {
if (filesToDelete.isEmpty()) {
return
}
if (provider != null && provider.isEnabled()) {
for (name in filesToDelete) {
errors.catch {
StorageUtil.delete(provider, fileSpec + '/' + name, roamingType)
}
}
}
else if (dir != null) {
var token: AccessToken? = null
try {
for (file in dir.getChildren()) {
if (filesToDelete.contains(file.getName())) {
if (token == null) {
token = WriteAction.start()
}
errors.catch {
file.delete(this)
}
}
}
}
finally {
if (token != null) {
token.finish()
}
}
}
filesToDelete.clear()
}
private fun getDirectory(): VirtualFile? {
var result = directory
if (result == null) {
result = LocalFileSystem.getInstance().findFileByIoFile(ioDirectory)
directory = result
}
return result
}
override fun getRootDirectory() = ioDirectory
override fun setSchemes(newSchemes: List<T>, removeCondition: Condition<T>?) {
if (removeCondition == null) {
schemes.clear()
}
else {
for (i in schemes.indices.reversed()) {
if (removeCondition.value(schemes.get(i))) {
schemes.remove(i)
}
}
}
retainExternalInfo(newSchemes)
schemes.addAll(newSchemes)
if (currentSchemeName != null) {
currentScheme = findSchemeByName(currentSchemeName!!)
if (currentScheme != null) {
return
}
}
currentScheme = schemes.firstOrNull()
currentSchemeName = currentScheme?.getName()
}
private fun retainExternalInfo(newSchemes: List<T>) {
if (schemeToInfo.isEmpty()) {
return
}
schemeToInfo.retainEntries(object : TObjectObjectProcedure<E, ExternalInfo> {
override fun execute(scheme: E, info: ExternalInfo): Boolean {
if (readOnlyExternalizableSchemes.get(scheme.getName()) == scheme) {
return true
}
for (t in newSchemes) {
// by identity
if (t === scheme) {
if (filesToDelete.isNotEmpty()) {
filesToDelete.remove("${info.fileName}")
}
return true
}
}
info.scheduleDelete()
return false
}
})
}
override fun addNewScheme(scheme: T, replaceExisting: Boolean) {
var toReplace = -1
for (i in schemes.indices) {
val existing = schemes.get(i)
if (existing.getName() == scheme.getName()) {
if (!Comparing.equal<Class<out Scheme>>(existing.javaClass, scheme.javaClass)) {
LOG.warn("'${scheme.getName()}' ${existing.javaClass.getSimpleName()} replaced with ${scheme.javaClass.getSimpleName()}")
}
toReplace = i
if (replaceExisting && existing is ExternalizableScheme) {
val oldInfo = schemeToInfo.remove(existing)
if (oldInfo != null && scheme is ExternalizableScheme && !schemeToInfo.containsKey(scheme)) {
@suppress("UNCHECKED_CAST")
schemeToInfo.put(scheme as E, oldInfo)
}
}
break
}
}
if (toReplace == -1) {
schemes.add(scheme)
}
else if (replaceExisting || scheme !is ExternalizableScheme) {
schemes.set(toReplace, scheme)
}
else {
scheme.renameScheme(UniqueNameGenerator.generateUniqueName(scheme.getName(), collectExistingNames(schemes)))
schemes.add(scheme)
}
if (scheme is ExternalizableScheme && filesToDelete.isNotEmpty()) {
val info = schemeToInfo.get(scheme)
if (info != null) {
filesToDelete.remove("${info.fileName}")
}
}
}
private fun collectExistingNames(schemes: Collection<T>): Collection<String> {
val result = THashSet<String>(schemes.size())
for (scheme in schemes) {
result.add(scheme.getName())
}
return result
}
override fun clearAllSchemes() {
schemeToInfo.forEachValue(object : TObjectProcedure<ExternalInfo> {
override fun execute(info: ExternalInfo): Boolean {
info.scheduleDelete()
return true
}
})
currentScheme = null
schemes.clear()
schemeToInfo.clear()
}
override fun getAllSchemes() = Collections.unmodifiableList(schemes)
override fun findSchemeByName(schemeName: String): T? {
for (scheme in schemes) {
if (scheme.getName() == schemeName) {
return scheme
}
}
return null
}
override fun setCurrentSchemeName(schemeName: String?) {
currentSchemeName = schemeName
currentScheme = null
}
override fun getCurrentScheme(): T? {
var scheme = currentScheme
if (scheme == null && currentSchemeName != null) {
scheme = findSchemeByName(currentSchemeName!!)
if (scheme != null) {
currentScheme = scheme
}
}
return scheme
}
override fun removeScheme(scheme: T) {
for (i in schemes.size() - 1 downTo 0) {
val s = schemes.get(i)
if (scheme.getName() == s.getName()) {
if (currentScheme == s) {
currentScheme = null
}
if (s is ExternalizableScheme) {
schemeToInfo.remove(s)?.scheduleDelete()
}
schemes.remove(i)
break
}
}
}
override fun getAllSchemeNames(): Collection<String> {
if (schemes.isEmpty()) {
return emptyList()
}
val names = ArrayList<String>(schemes.size())
for (scheme in schemes) {
names.add(scheme.getName())
}
return names
}
override fun isMetadataEditable(scheme: E) = !readOnlyExternalizableSchemes.containsKey(scheme.name)
private class ExternalInfo(var fileNameWithoutExtension: String, var fileExtension: String?) {
// we keep it to detect rename
var schemeName: String? = null
var hash: Int = 0
val fileName: String
get() = "$fileNameWithoutExtension$fileExtension"
fun setFileNameWithoutExtension(nameWithoutExtension: String, extension: String) {
fileNameWithoutExtension = nameWithoutExtension
fileExtension = extension
}
override fun toString() = fileName
}
override fun toString() = fileSpec
}
private fun ExternalizableScheme.renameScheme(newName: String) {
if (newName != getName()) {
setName(newName)
LOG.assertTrue(newName == getName())
}
}
private inline fun MutableList<Throwable>.catch(runnable: () -> Unit) {
try {
runnable()
}
catch (e: Throwable) {
add(e)
}
}
inline val Scheme.name: String
get() = getName()
@@ -34,7 +34,7 @@ import java.util.List;
public final class SchemesManagerFactoryImpl extends SchemesManagerFactory implements SettingsSavingComponent {
private static final Logger LOG = Logger.getInstance(SchemesManagerFactoryImpl.class);
private final List<SchemesManagerImpl> myRegisteredManagers = ContainerUtil.createLockFreeCopyOnWriteList();
private final List<SchemeManagerImpl> myRegisteredManagers = ContainerUtil.createLockFreeCopyOnWriteList();
@NotNull
@Override
@@ -44,13 +44,13 @@ public final class SchemesManagerFactoryImpl extends SchemesManagerFactory imple
StateStorageManager storageManager = ((ApplicationImpl)ApplicationManager.getApplication()).getStateStore().getStateStorageManager();
String baseDirPath = storageManager.expandMacros(fileSpec);
StreamProvider provider = storageManager.getStreamProvider();
SchemesManagerImpl<T, E> manager = new SchemesManagerImpl<T, E>(fileSpec, processor, roamingType, provider, new File(baseDirPath));
SchemeManagerImpl<T, E> manager = new SchemeManagerImpl<T, E>(fileSpec, processor, roamingType, provider, new File(baseDirPath));
myRegisteredManagers.add(manager);
return manager;
}
public void process(@NotNull Consumer<SchemesManagerImpl> processor) {
for (SchemesManagerImpl manager : myRegisteredManagers) {
public void process(@NotNull Consumer<SchemeManagerImpl> processor) {
for (SchemeManagerImpl manager : myRegisteredManagers) {
try {
processor.consume(manager);
}
@@ -62,15 +62,12 @@ public final class SchemesManagerFactoryImpl extends SchemesManagerFactory imple
@Override
public void save() {
List<Throwable> errors = null;
for (SchemesManager registeredManager : myRegisteredManagers) {
List<Throwable> errors = new SmartList<Throwable>();
for (SchemeManagerImpl registeredManager : myRegisteredManagers) {
try {
registeredManager.save();
registeredManager.save(errors);
}
catch (Throwable e) {
if (errors == null) {
errors = new SmartList<Throwable>();
}
errors.add(e);
}
}
@@ -1,949 +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.options;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.application.ex.DecodeDefaultsUtil;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.impl.stores.DirectoryBasedStorage;
import com.intellij.openapi.components.impl.stores.DirectoryStorageData;
import com.intellij.openapi.components.impl.stores.StorageUtil;
import com.intellij.openapi.components.impl.stores.StreamProvider;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.AbstractExtensionPointBean;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.tracker.VirtualFileTracker;
import com.intellij.util.PathUtil;
import com.intellij.util.PathUtilRt;
import com.intellij.util.SmartList;
import com.intellij.util.ThrowableConvertor;
import com.intellij.util.io.URLUtil;
import com.intellij.util.lang.CompoundRuntimeException;
import com.intellij.util.text.UniqueNameGenerator;
import gnu.trove.*;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Parent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.*;
public final class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme> extends SchemesManager<T, E> implements SafeWriteRequestor {
private static final Logger LOG = Logger.getInstance(SchemesManagerFactoryImpl.class);
private final ArrayList<T> mySchemes = new ArrayList<T>();
private volatile T myCurrentScheme;
@Nullable
private String myCurrentSchemeName;
private final String myFileSpec;
private final SchemeProcessor<E> myProcessor;
private final RoamingType myRoamingType;
private final StreamProvider provider;
private final File myIoDir;
private VirtualFile myDir;
private final String mySchemeExtension;
private final boolean myUpdateExtension;
private final Set<String> filesToDelete = new THashSet<String>();
// scheme could be changed - so, hashcode will be changed - we must use identity hashing strategy
@SuppressWarnings("unchecked")
private final THashMap<ExternalizableScheme, ExternalInfo> schemeToInfo = new THashMap<ExternalizableScheme, ExternalInfo>(TObjectHashingStrategy.IDENTITY);
public SchemesManagerImpl(@NotNull String fileSpec,
@NotNull SchemeProcessor<E> processor,
@NotNull RoamingType roamingType,
@Nullable StreamProvider provider,
@NotNull File baseDir) {
myFileSpec = fileSpec;
myProcessor = processor;
myRoamingType = roamingType;
this.provider = provider;
myIoDir = baseDir;
if (processor instanceof SchemeExtensionProvider) {
mySchemeExtension = ((SchemeExtensionProvider)processor).getSchemeExtension();
myUpdateExtension = ((SchemeExtensionProvider)processor).isUpgradeNeeded();
}
else {
mySchemeExtension = DirectoryStorageData.DEFAULT_EXT;
myUpdateExtension = false;
}
Application application = ApplicationManager.getApplication();
VirtualFileTracker virtualFileTracker = application == null ? null : ServiceManager.getService(VirtualFileTracker.class);
if (virtualFileTracker != null) {
final String baseDirPath = myIoDir.getAbsolutePath().replace(File.separatorChar, '/');
virtualFileTracker.addTracker(LocalFileSystem.PROTOCOL_PREFIX + baseDirPath, new VirtualFileAdapter() {
@Override
public void contentsChanged(@NotNull VirtualFileEvent event) {
if (event.getRequestor() != null || !isMy(event)) {
return;
}
E scheme = findExternalizableSchemeByFileName(event.getFile().getName());
T oldCurrentScheme = null;
if (scheme != null) {
oldCurrentScheme = getCurrentScheme();
//noinspection unchecked
removeScheme((T)scheme);
myProcessor.onSchemeDeleted(scheme);
}
E readScheme = readSchemeFromFile(event.getFile(), null);
if (readScheme != null) {
myProcessor.initScheme(readScheme);
myProcessor.onSchemeAdded(readScheme);
T newCurrentScheme = getCurrentScheme();
if (oldCurrentScheme != null && newCurrentScheme == null) {
setCurrentSchemeName(readScheme.getName());
newCurrentScheme = getCurrentScheme();
}
if (oldCurrentScheme != newCurrentScheme) {
myProcessor.onCurrentSchemeChanged(oldCurrentScheme);
}
}
}
@Override
public void fileCreated(@NotNull VirtualFileEvent event) {
if (event.getRequestor() == null) {
if (event.getFile().isDirectory()) {
VirtualFile dir = getDirectory();
if (event.getFile().equals(dir)) {
for (VirtualFile file : dir.getChildren()) {
if (isMy(file)) {
schemeCreatedExternally(file);
}
}
}
}
else if (isMy(event)) {
schemeCreatedExternally(event.getFile());
}
}
}
private void schemeCreatedExternally(@NotNull VirtualFile file) {
E readScheme = readSchemeFromFile(file, null);
if (readScheme != null) {
myProcessor.initScheme(readScheme);
myProcessor.onSchemeAdded(readScheme);
}
}
@Override
public void fileDeleted(@NotNull VirtualFileEvent event) {
if (event.getRequestor() == null) {
if (event.getFile().isDirectory()) {
VirtualFile dir = myDir;
if (event.getFile().equals(dir)) {
myDir = null;
for (VirtualFile file : dir.getChildren()) {
if (isMy(file)) {
schemeDeletedExternally(file);
}
}
}
}
else if (isMy(event)) {
schemeDeletedExternally(event.getFile());
}
}
}
private void schemeDeletedExternally(@NotNull VirtualFile file) {
E scheme = findExternalizableSchemeByFileName(file.getName());
T oldCurrentScheme = null;
if (scheme != null) {
oldCurrentScheme = getCurrentScheme();
//noinspection unchecked
removeScheme((T)scheme);
myProcessor.onSchemeDeleted(scheme);
}
T newCurrentScheme = getCurrentScheme();
if (oldCurrentScheme != null && newCurrentScheme == null) {
if (!mySchemes.isEmpty()) {
setCurrentSchemeName(mySchemes.get(0).getName());
newCurrentScheme = getCurrentScheme();
}
}
if (oldCurrentScheme != newCurrentScheme) {
myProcessor.onCurrentSchemeChanged(oldCurrentScheme);
}
}
}, false, application);
}
}
@Override
public void loadBundledScheme(@NotNull String resourceName, @NotNull Object requestor, @NotNull ThrowableConvertor<Element, T, Throwable> convertor) {
try {
URL url = requestor instanceof AbstractExtensionPointBean
? (((AbstractExtensionPointBean)requestor).getLoaderForClass().getResource(resourceName))
: DecodeDefaultsUtil.getDefaults(requestor, resourceName);
if (url == null) {
// Error shouldn't occur during this operation thus we report error instead of info
LOG.error("Cannot read scheme from " + resourceName);
return;
}
Element element = JDOMUtil.load(URLUtil.openStream(url));
T scheme = convertor.convert(element);
if (scheme instanceof ExternalizableScheme) {
String fileName = PathUtilRt.getFileName(url.getPath());
String extension = getFileExtension(fileName, true);
ExternalInfo info = new ExternalInfo(fileName.substring(0, fileName.length() - extension.length()), extension);
info.hash = JDOMUtil.getTreeHash(element, true);
info.schemeName = scheme.getName();
ExternalInfo oldInfo = schemeToInfo.put((ExternalizableScheme)scheme, info);
LOG.assertTrue(oldInfo == null);
}
addNewScheme(scheme, false);
}
catch (Throwable e) {
LOG.error("Cannot read scheme from " + resourceName, e);
}
}
@NotNull
private String getFileExtension(@NotNull CharSequence fileName, boolean allowAny) {
String extension;
if (StringUtilRt.endsWithIgnoreCase(fileName, mySchemeExtension)) {
extension = mySchemeExtension;
}
else if (StringUtilRt.endsWithIgnoreCase(fileName, DirectoryStorageData.DEFAULT_EXT)) {
extension = DirectoryStorageData.DEFAULT_EXT;
}
else if (allowAny) {
extension = PathUtil.getFileExtension(fileName.toString());
LOG.assertTrue(extension != null);
}
else {
throw new IllegalStateException("Scheme file extension " + fileName + " is unknown, must be filtered out");
}
return extension;
}
private boolean isMy(@NotNull VirtualFileEvent event) {
return isMy(event.getFile());
}
private boolean isMy(@NotNull VirtualFile file) {
return StringUtilRt.endsWithIgnoreCase(file.getNameSequence(), mySchemeExtension);
}
@Override
@NotNull
public Collection<E> loadSchemes() {
final Map<String, E> result = new LinkedHashMap<String, E>();
if (provider != null && provider.isEnabled()) {
provider.processChildren(myFileSpec, myRoamingType, new Condition<String>() {
@Override
public boolean value(@NotNull String name) {
return canRead(name);
}
}, new StreamProvider.ChildrenProcessor() {
@Override
public boolean process(@NotNull String name, @NotNull InputStream input) {
loadScheme(name, input, result);
return true;
}
});
}
else {
VirtualFile dir = getDirectory();
VirtualFile[] files = dir == null ? null : dir.getChildren();
if (files != null) {
for (VirtualFile file : files) {
readSchemeFromFile(file, result);
}
}
}
Collection<E> list = result.values();
for (E scheme : list) {
myProcessor.initScheme(scheme);
}
return list;
}
public void reload() {
// we must not remove non-persistent (e.g. predefined) schemes, because we cannot load it (obviously)
for (int i = mySchemes.size() - 1; i >= 0; i--) {
T scheme = mySchemes.get(i);
//noinspection unchecked
if (scheme instanceof ExternalizableScheme && getState(((E)scheme)) != BaseSchemeProcessor.State.NON_PERSISTENT) {
mySchemes.remove(i);
if (scheme == myCurrentScheme) {
myCurrentScheme = null;
}
}
}
retainExternalInfo(mySchemes);
loadSchemes();
}
@Nullable
private E findExternalizableSchemeByFileName(@NotNull String fileName) {
for (T scheme : mySchemes) {
if (scheme instanceof ExternalizableScheme) {
if (fileName.equals((getFileName((ExternalizableScheme)scheme)) + mySchemeExtension)) {
//noinspection CastConflictsWithInstanceof,unchecked
return (E)scheme;
}
}
}
return null;
}
private boolean isOverwriteOnLoad(@NotNull T existingScheme) {
if (existingScheme instanceof ExternalizableScheme) {
ExternalInfo info = schemeToInfo.get(existingScheme);
if (info != null && !mySchemeExtension.equals(info.fileExtension)) {
// scheme from file with old extension, so, we must ignore it
return true;
}
}
return false;
}
@Nullable
private E loadScheme(@NotNull CharSequence fileName, @NotNull InputStream input, @Nullable Map<String, E> result) {
boolean duringLoad = result != null;
try {
Element element = JDOMUtil.load(input);
E scheme;
if (myProcessor instanceof BaseSchemeProcessor) {
scheme = ((BaseSchemeProcessor<E>)myProcessor).readScheme(element, duringLoad);
}
else {
//noinspection deprecation
scheme = myProcessor.readScheme(new Document((Element)element.detach()));
}
if (scheme == null) {
return null;
}
String extension = getFileExtension(fileName, false);
String fileNameWithoutExtension = fileName.subSequence(0, fileName.length() - extension.length()).toString();
if (duringLoad) {
if (filesToDelete.contains(fileName.toString())) {
LOG.warn("Scheme file " + fileName + " is not loaded because marked to delete");
return null;
}
T existingScheme = findSchemeByName(scheme.getName());
if (existingScheme != null) {
if (isOverwriteOnLoad(existingScheme)) {
removeScheme(existingScheme);
}
else {
// We don't load scheme with duplicated name - if we generate unique name for it, it will be saved then with new name.
// It is not what all can expect. Such situation in most cases indicates error on previous level, so, we just warn about it.
LOG.warn("Scheme file " + fileName + " is not loaded because defines duplicated name " + scheme.getName());
return null;
}
}
}
ExternalInfo info = schemeToInfo.get(scheme);
if (info == null) {
info = new ExternalInfo(fileNameWithoutExtension, extension);
schemeToInfo.put(scheme, info);
}
else {
info.setFileNameWithoutExtension(fileNameWithoutExtension, extension);
}
info.hash = JDOMUtil.getTreeHash(element, true);
info.schemeName = scheme.getName();
//noinspection unchecked
T s = (T)scheme;
if (duringLoad) {
mySchemes.add(s);
}
else {
addScheme(s);
}
if (result != null) {
result.put(scheme.getName(), scheme);
}
return scheme;
}
catch (Exception e) {
LOG.error("Cannot read scheme " + fileName, e);
}
return null;
}
@Nullable
private String getFileName(@NotNull ExternalizableScheme scheme) {
ExternalInfo info = schemeToInfo.get(scheme);
return info == null ? null : info.fileNameWithoutExtension;
}
private boolean canRead(@NotNull CharSequence name) {
return myUpdateExtension && StringUtilRt.endsWithIgnoreCase(name, DirectoryStorageData.DEFAULT_EXT) || StringUtilRt.endsWithIgnoreCase(name, mySchemeExtension);
}
@Nullable
private E readSchemeFromFile(@NotNull VirtualFile file, @Nullable Map<String, E> result) {
CharSequence fileName = file.getNameSequence();
if (file.isDirectory() || !canRead(fileName)) {
return null;
}
try {
return loadScheme(fileName, file.getInputStream(), result);
}
catch (IOException e) {
LOG.error("Cannot read scheme " + fileName, e);
}
return null;
}
@Override
public void save() {
boolean hasSchemes = false;
UniqueNameGenerator nameGenerator = new UniqueNameGenerator();
List<E> schemesToSave = new SmartList<E>();
for (T scheme : mySchemes) {
if (scheme instanceof ExternalizableScheme) {
//noinspection CastConflictsWithInstanceof,unchecked
E eScheme = (E)scheme;
BaseSchemeProcessor.State state = getState(eScheme);
if (state == BaseSchemeProcessor.State.NON_PERSISTENT) {
continue;
}
hasSchemes = true;
if (state != BaseSchemeProcessor.State.UNCHANGED) {
schemesToSave.add(eScheme);
}
String fileName = getFileName(eScheme);
if (fileName != null && !isRenamed(eScheme)) {
nameGenerator.addExistingName(fileName);
}
}
}
List<Throwable> errors = null;
for (E scheme : schemesToSave) {
try {
saveScheme(scheme, nameGenerator);
}
catch (Throwable e) {
if (errors == null) {
errors = new SmartList<Throwable>();
}
errors.add(e);
}
}
VirtualFile dir = getDirectory();
errors = deleteFiles(dir, errors);
if (!hasSchemes && dir != null) {
errors = removeDirectoryIfEmpty(dir, errors);
}
CompoundRuntimeException.doThrow(errors);
}
@Nullable
private List<Throwable> removeDirectoryIfEmpty(@NotNull VirtualFile dir, @Nullable List<Throwable> errors) {
for (VirtualFile file : dir.getChildren()) {
if (!file.is(VFileProperty.HIDDEN)) {
LOG.info("Directory " + dir.getNameSequence() + " is not deleted: at least one file " + file.getNameSequence() + " exists");
return errors;
}
}
LOG.info("Remove schemes directory " + dir.getNameSequence());
myDir = null;
AccessToken token = WriteAction.start();
try {
dir.delete(this);
}
catch (Throwable e) {
if (errors == null) {
errors = new SmartList<Throwable>();
}
errors.add(e);
}
finally {
token.finish();
}
return errors;
}
@NotNull
private BaseSchemeProcessor.State getState(@NotNull E scheme) {
if (myProcessor instanceof BaseSchemeProcessor) {
return ((BaseSchemeProcessor<E>)myProcessor).getState(scheme);
}
else {
//noinspection deprecation
return myProcessor.shouldBeSaved(scheme) ? BaseSchemeProcessor.State.POSSIBLY_CHANGED : BaseSchemeProcessor.State.NON_PERSISTENT;
}
}
private void saveScheme(@NotNull E scheme, @NotNull UniqueNameGenerator nameGenerator) throws WriteExternalException, IOException {
@Nullable
ExternalInfo externalInfo = schemeToInfo.get(scheme);
String currentFileNameWithoutExtension = externalInfo == null ? null : externalInfo.fileNameWithoutExtension;
Parent parent = myProcessor.writeScheme(scheme);
Element element = parent == null || parent instanceof Element ? (Element)parent : ((Document)parent).detachRootElement();
if (JDOMUtil.isEmpty(element)) {
if (externalInfo != null) {
scheduleDelete(externalInfo);
}
return;
}
String fileNameWithoutExtension = currentFileNameWithoutExtension;
if (fileNameWithoutExtension == null || isRenamed(scheme)) {
fileNameWithoutExtension = nameGenerator.generateUniqueName(FileUtil.sanitizeName(scheme.getName()));
}
int newHash = JDOMUtil.getTreeHash(element, true);
if (externalInfo != null && currentFileNameWithoutExtension == fileNameWithoutExtension && newHash == externalInfo.hash) {
return;
}
String fileName = fileNameWithoutExtension + mySchemeExtension;
// file will be overwritten, so, we don't need to delete it
filesToDelete.remove(fileName);
// stream provider always use LF separator
BufferExposingByteArrayOutputStream byteOut = StorageUtil.writeToBytes(element, "\n");
String providerPath;
if (provider != null && provider.isEnabled()) {
providerPath = myFileSpec + '/' + fileName;
if (!provider.isApplicable(providerPath, myRoamingType)) {
providerPath = null;
}
}
else {
providerPath = null;
}
// if another new scheme uses old name of this scheme, so, we must not delete it (as part of rename operation)
boolean renamed = externalInfo != null && fileNameWithoutExtension != currentFileNameWithoutExtension && nameGenerator.value(currentFileNameWithoutExtension);
if (providerPath == null) {
VirtualFile file = null;
VirtualFile dir = getDirectory();
if (dir == null || !dir.isValid()) {
dir = DirectoryBasedStorage.createDir(myIoDir, this);
myDir = dir;
}
if (renamed) {
file = dir.findChild(externalInfo.fileNameWithoutExtension + externalInfo.fileExtension);
if (file != null) {
AccessToken token = WriteAction.start();
try {
file.rename(this, fileName);
}
finally {
token.finish();
}
}
}
if (file == null) {
file = DirectoryBasedStorage.getFile(fileName, dir, this);
}
AccessToken token = WriteAction.start();
try {
OutputStream out = file.getOutputStream(this);
try {
byteOut.writeTo(out);
}
finally {
out.close();
}
}
finally {
token.finish();
}
}
else {
if (renamed) {
scheduleDelete(externalInfo);
}
provider.saveContent(providerPath, byteOut.getInternalBuffer(), byteOut.size(), myRoamingType);
}
if (externalInfo == null) {
externalInfo = new ExternalInfo(fileNameWithoutExtension, mySchemeExtension);
schemeToInfo.put(scheme, externalInfo);
}
else {
externalInfo.setFileNameWithoutExtension(fileNameWithoutExtension, mySchemeExtension);
}
externalInfo.hash = newHash;
externalInfo.schemeName = scheme.getName();
}
private void scheduleDelete(@NotNull ExternalInfo externalInfo) {
filesToDelete.add(externalInfo.fileNameWithoutExtension + externalInfo.fileExtension);
}
private boolean isRenamed(@NotNull ExternalizableScheme scheme) {
ExternalInfo info = schemeToInfo.get(scheme);
return info != null && !scheme.getName().equals(info.schemeName);
}
@Nullable
private List<Throwable> deleteFiles(@Nullable VirtualFile dir, List<Throwable> errors) {
if (filesToDelete.isEmpty()) {
return errors;
}
if (provider != null && provider.isEnabled()) {
for (String name : filesToDelete) {
try {
StorageUtil.delete(provider, myFileSpec + '/' + name, myRoamingType);
}
catch (Throwable e) {
if (errors == null) {
errors = new SmartList<Throwable>();
}
errors.add(e);
}
}
}
else if (dir != null) {
AccessToken token = null;
try {
for (VirtualFile file : dir.getChildren()) {
if (filesToDelete.contains(file.getName())) {
if (token == null) {
token = WriteAction.start();
}
try {
file.delete(this);
}
catch (IOException e) {
if (errors == null) {
errors = new SmartList<Throwable>();
}
errors.add(e);
}
}
}
}
finally {
if (token != null) {
token.finish();
}
}
}
filesToDelete.clear();
return errors;
}
@Nullable
private VirtualFile getDirectory() {
VirtualFile result = myDir;
if (result == null) {
result = LocalFileSystem.getInstance().findFileByIoFile(myIoDir);
myDir = result;
}
return result;
}
@Override
public File getRootDirectory() {
return myIoDir;
}
private void schemeAdded(@NotNull T scheme) {
if (!(scheme instanceof ExternalizableScheme)) {
return;
}
ExternalInfo info = schemeToInfo.get(scheme);
if (info != null) {
filesToDelete.remove(info.fileNameWithoutExtension + info.fileExtension);
}
}
@Override
public void setSchemes(@NotNull final List<T> newSchemes, @Nullable Condition<T> removeCondition) {
if (removeCondition == null) {
mySchemes.clear();
}
else {
for (int i = mySchemes.size() - 1; i >= 0; i--) {
T scheme = mySchemes.get(i);
if (removeCondition.value(scheme)) {
mySchemes.remove(i);
}
}
}
retainExternalInfo(newSchemes);
mySchemes.ensureCapacity(newSchemes.size());
for (T scheme : newSchemes) {
mySchemes.add(scheme);
schemeAdded(scheme);
}
if (myCurrentSchemeName != null) {
myCurrentScheme = findSchemeByName(myCurrentSchemeName);
if (myCurrentScheme != null) {
return;
}
}
myCurrentScheme = mySchemes.isEmpty() ? null : mySchemes.get(0);
myCurrentSchemeName = myCurrentScheme == null ? null : myCurrentScheme.getName();
}
private void retainExternalInfo(@NotNull final List<T> schemes) {
if (schemeToInfo.isEmpty()) {
return;
}
schemeToInfo.retainEntries(new TObjectObjectProcedure<ExternalizableScheme, ExternalInfo>() {
@Override
public boolean execute(ExternalizableScheme scheme, ExternalInfo info) {
for (T t : schemes) {
if (t == scheme) {
return true;
}
}
scheduleDelete(info);
return false;
}
});
}
@Override
public void addNewScheme(@NotNull T scheme, boolean replaceExisting) {
int toReplace = -1;
for (int i = 0; i < mySchemes.size(); i++) {
T existing = mySchemes.get(i);
if (existing.getName().equals(scheme.getName())) {
if (!Comparing.equal(existing.getClass(), scheme.getClass())) {
LOG.warn("'" + scheme.getName() + "' " + existing.getClass().getSimpleName() + " replaced with " + scheme.getClass().getSimpleName());
}
toReplace = i;
if (replaceExisting && existing instanceof ExternalizableScheme) {
ExternalInfo oldInfo = schemeToInfo.remove((ExternalizableScheme)existing);
if (oldInfo != null && scheme instanceof ExternalizableScheme) {
ExternalInfo newInfo = schemeToInfo.get((ExternalizableScheme)scheme);
if (newInfo == null) {
schemeToInfo.put((ExternalizableScheme)scheme, oldInfo);
}
}
}
break;
}
}
if (toReplace == -1) {
mySchemes.add(scheme);
}
else if (replaceExisting || !(scheme instanceof ExternalizableScheme)) {
mySchemes.set(toReplace, scheme);
}
else {
//noinspection unchecked
renameScheme((ExternalizableScheme)scheme, UniqueNameGenerator.generateUniqueName(scheme.getName(), collectExistingNames(mySchemes)));
mySchemes.add(scheme);
}
schemeAdded(scheme);
}
@NotNull
private Collection<String> collectExistingNames(@NotNull Collection<T> schemes) {
Set<String> result = new THashSet<String>(schemes.size());
for (T scheme : schemes) {
result.add(scheme.getName());
}
return result;
}
@Override
public void clearAllSchemes() {
schemeToInfo.forEachValue(new TObjectProcedure<ExternalInfo>() {
@Override
public boolean execute(ExternalInfo info) {
scheduleDelete(info);
return true;
}
});
myCurrentScheme = null;
mySchemes.clear();
schemeToInfo.clear();
}
@Override
@NotNull
public List<T> getAllSchemes() {
return Collections.unmodifiableList(mySchemes);
}
@Override
@Nullable
public T findSchemeByName(@NotNull String schemeName) {
for (T scheme : mySchemes) {
if (scheme.getName().equals(schemeName)) {
return scheme;
}
}
return null;
}
@Override
public void setCurrentSchemeName(@Nullable String schemeName) {
myCurrentSchemeName = schemeName;
myCurrentScheme = null;
}
@Override
@Nullable
public T getCurrentScheme() {
T scheme = myCurrentScheme;
if (scheme == null && myCurrentSchemeName != null) {
scheme = findSchemeByName(myCurrentSchemeName);
if (scheme != null) {
myCurrentScheme = scheme;
}
}
return scheme;
}
@Override
public void removeScheme(@NotNull T scheme) {
for (int i = 0, n = mySchemes.size(); i < n; i++) {
T s = mySchemes.get(i);
if (scheme.getName().equals(s.getName())) {
if (myCurrentScheme == s) {
myCurrentScheme = null;
}
if (s instanceof ExternalizableScheme) {
ExternalInfo info = schemeToInfo.remove((ExternalizableScheme)s);
if (info != null) {
scheduleDelete(info);
}
}
mySchemes.remove(i);
break;
}
}
}
@Override
@NotNull
public Collection<String> getAllSchemeNames() {
if (mySchemes.isEmpty()) {
return Collections.emptyList();
}
List<String> names = new ArrayList<String>(mySchemes.size());
for (T scheme : mySchemes) {
names.add(scheme.getName());
}
return names;
}
private static void renameScheme(@NotNull ExternalizableScheme scheme, @NotNull String newName) {
if (!newName.equals(scheme.getName())) {
scheme.setName(newName);
LOG.assertTrue(newName.equals(scheme.getName()));
}
}
private static final class ExternalInfo {
public ExternalInfo(@NotNull String fileNameWithoutExtension, @NotNull String fileExtension) {
this.fileNameWithoutExtension = fileNameWithoutExtension;
this.fileExtension = fileExtension;
}
// we keep it to detect rename
private String schemeName;
@NotNull
private String fileNameWithoutExtension;
private int hash;
private String fileExtension;
public void setFileNameWithoutExtension(@NotNull String nameWithoutExtension, @NotNull String extension) {
fileNameWithoutExtension = nameWithoutExtension;
fileExtension = extension;
}
@Override
public String toString() {
return fileNameWithoutExtension;
}
}
@Override
public String toString() {
return myFileSpec;
}
}
@@ -47,7 +47,7 @@ public class SingleConfigurableEditor extends DialogWrapper {
private JComponent myCenterPanel;
private final String myDimensionKey;
private final boolean myShowApplyButton;
private boolean myChangesWereApplied;
private boolean mySaveAllOnClose;
public SingleConfigurableEditor(@Nullable Project project,
Configurable configurable,
@@ -166,18 +166,16 @@ public class SingleConfigurableEditor extends DialogWrapper {
@Override
public void doCancelAction() {
if (myChangesWereApplied) {
ApplicationManager.getApplication().saveAll();
}
super.doCancelAction();
}
@Override
protected void doOKAction() {
try {
if (myConfigurable.isModified()) myConfigurable.apply();
ApplicationManager.getApplication().saveAll();
if (myConfigurable.isModified()) {
myConfigurable.apply();
mySaveAllOnClose = true;
}
}
catch (ConfigurationException e) {
if (e.getMessage() != null) {
@@ -237,7 +235,7 @@ public class SingleConfigurableEditor extends DialogWrapper {
myPerformAction = true;
if (myConfigurable.isModified()) {
myConfigurable.apply();
myChangesWereApplied = true;
mySaveAllOnClose = true;
setCancelButtonText(CommonBundle.getCloseButtonText());
}
}
@@ -275,5 +273,9 @@ public class SingleConfigurableEditor extends DialogWrapper {
super.dispose();
myConfigurable.disposeUIResources();
myConfigurable = null;
if (mySaveAllOnClose) {
ApplicationManager.getApplication().saveAll();
}
}
}
@@ -108,9 +108,9 @@ public class ProgressManagerImpl extends CoreProgressManager implements Disposab
@Override
@NotNull
public Future<?> runProcessWithProgressAsynchronously(@NotNull final Task.Backgroundable task,
@NotNull final ProgressIndicator progressIndicator,
@Nullable final Runnable continuation,
@NotNull final ModalityState modalityState) {
@NotNull final ProgressIndicator progressIndicator,
@Nullable final Runnable continuation,
@NotNull final ModalityState modalityState) {
if (progressIndicator instanceof Disposable) {
Disposer.register(ApplicationManager.getApplication(), (Disposable)progressIndicator);
}
@@ -17,6 +17,7 @@ package com.intellij.openapi.progress.util;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.DialogWrapperPeer;
@@ -221,13 +222,17 @@ class ProgressDialog implements Disposable {
}
void cancel() {
enableCancelButtonIfNeeded(false);
}
void enableCancelButtonIfNeeded(final boolean enable) {
if (myProgressWindow.myShouldShowCancel) {
SwingUtilities.invokeLater(new Runnable() {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
myCancelButton.setEnabled(false);
myCancelButton.setEnabled(enable);
}
});
}, ModalityState.any());
}
}
@@ -241,6 +241,22 @@ public class ProgressWindow extends ProgressIndicatorBase implements BlockingPro
}
}
@Override
public void startNonCancelableSection() {
if (isCancelable()) {
enableCancel(false);
}
super.startNonCancelableSection();
}
@Override
public void finishNonCancelableSection() {
super.finishNonCancelableSection();
if (isCancelable()) {
enableCancel(true);
}
}
@Override
public void setIndeterminate(boolean indeterminate) {
super.setIndeterminate(indeterminate);
@@ -384,4 +400,8 @@ public class ProgressWindow extends ProgressIndicatorBase implements BlockingPro
public boolean isPopupWasShown() {
return myDialog != null && myDialog.myPopup != null && myDialog.myPopup.isShowing();
}
protected void enableCancel(boolean enable) {
myDialog.enableCancelButtonIfNeeded(enable);
}
}
@@ -60,13 +60,13 @@ public class FileNameCache {
private static int calcStripeIdFromNameId(int id) {
int h = id;
h -= (h<<6);
h ^= (h>>17);
h -= (h<<9);
h ^= (h<<4);
h -= (h<<3);
h ^= (h<<10);
h ^= (h>>15);
h -= h<<6;
h ^= h>>17;
h -= h<<9;
h ^= h<<4;
h -= h<<3;
h ^= h<<10;
h ^= h>>15;
return h % ourNameCache.length;
}
@@ -18,6 +18,7 @@ package com.intellij.openapi.vfs.newvfs.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileAttributes;
import com.intellij.openapi.util.io.FileUtilRt;
@@ -32,6 +33,7 @@ import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
import com.intellij.openapi.vfs.newvfs.persistent.FSRecords;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl;
import com.intellij.psi.impl.PsiCachedValue;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.UriUtil;
@@ -547,6 +549,15 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
@Override
protected boolean changeUserMap(KeyFMap oldMap, KeyFMap newMap) {
checkLeaks(newMap);
return myData.changeUserMap(oldMap, UserDataInterner.internUserData(newMap));
}
static void checkLeaks(KeyFMap newMap) {
for (Key key : newMap.getKeys()) {
if (key != null && newMap.get(key) instanceof PsiCachedValue) {
throw new AssertionError("Don't store CachedValue in VFS user data, since it leads to memory leaks");
}
}
}
}
@@ -142,6 +142,7 @@ public class VirtualFileImpl extends VirtualFileSystemEntry {
@Override
protected boolean changeUserMap(KeyFMap oldMap, KeyFMap newMap) {
VirtualDirectoryImpl.checkLeaks(newMap);
return mySegment.changeUserMap(Math.abs(getId()), oldMap, UserDataInterner.internUserData(newMap));
}
@@ -25,6 +25,7 @@ import gnu.trove.TObjectObjectProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.List;
public abstract class CollectionModelEditor<T, E extends CollectionItemEditor<T>> implements ElementProducer<T> {
@@ -129,10 +130,7 @@ public abstract class CollectionModelEditor<T, E extends CollectionItemEditor<T>
mutable = itemEditor.clone(item, false);
modifiedToOriginal.put(mutable, item);
originalToModified.put(item, mutable);
List<T> items = getItems();
// silently replace item
items.set(index == -1 ? ContainerUtil.indexOfIdentity(items, item) : index, mutable);
silentlyReplaceItem(item, mutable, index);
}
return mutable;
}
@@ -146,4 +144,26 @@ public abstract class CollectionModelEditor<T, E extends CollectionItemEditor<T>
modifiedToOriginal.forEachEntry(procedure);
}
}
protected void silentlyReplaceItem(@NotNull T oldItem, @NotNull T newItem, int index) {
// silently replace item
List<T> items = getItems();
items.set(index == -1 ? ContainerUtil.indexOfIdentity(items, oldItem) : index, newItem);
}
protected final boolean areSelectedItemsRemovable(@NotNull ListSelectionModel selectionMode) {
int minSelectionIndex = selectionMode.getMinSelectionIndex();
int maxSelectionIndex = selectionMode.getMaxSelectionIndex();
if (minSelectionIndex < 0 || maxSelectionIndex < 0) {
return false;
}
List<T> items = getItems();
for (int i = minSelectionIndex; i <= maxSelectionIndex; i++) {
if (itemEditor.isRemovable(items.get(i))) {
return true;
}
}
return false;
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.util.ui;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import com.intellij.util.containers.ContainerUtil;
@@ -29,19 +30,19 @@ public class ListModelEditor<T> extends ListModelEditorBase<T> {
private JBList list = new JBList(model);
public ListModelEditor(@NotNull final ListItemEditor<T> itemEditor) {
public ListModelEditor(@NotNull ListItemEditor<T> itemEditor) {
super(itemEditor);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setCellRenderer(new MyListCellRenderer());
toolbarDecorator = ToolbarDecorator.createDecorator(list)
toolbarDecorator = ToolbarDecorator.createDecorator(list, model)
.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
if (!model.isEmpty()) {
T lastItem = model.getElementAt(model.getSize() - 1);
if (itemEditor.isEmpty(lastItem)) {
if (ListModelEditor.this.itemEditor.isEmpty(lastItem)) {
ListScrollingUtil.selectItem(list, ContainerUtil.indexOfIdentity(model.getItems(), lastItem));
return;
}
@@ -51,7 +52,19 @@ public class ListModelEditor<T> extends ListModelEditorBase<T> {
model.add(item);
ListScrollingUtil.selectItem(list, ContainerUtil.indexOfIdentity(model.getItems(), item));
}
});
})
.setRemoveActionUpdater(new AnActionButtonUpdater() {
@Override
public boolean isEnabled(AnActionEvent e) {
return areSelectedItemsRemovable(list.getSelectionModel());
}
});
}
@NotNull
public ListModelEditor<T> disableUpDownActions() {
toolbarDecorator.disableUpDownActions();
return this;
}
@NotNull
@@ -19,7 +19,6 @@ import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.CollectionListModel;
import com.intellij.ui.MutableCollectionComboBoxModel;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TObjectObjectProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -85,11 +84,7 @@ public abstract class ListModelEditorBase<T> extends CollectionModelEditor<T, Li
@Override
public boolean execute(T newItem, T oldItem) {
itemEditor.applyModifiedProperties(newItem, oldItem);
int index = ContainerUtil.indexOfIdentity(items, newItem);
if (index == -1) {
LOG.error("Inconsistent model", newItem.toString());
}
model.setElementAt(oldItem, index);
silentlyReplaceItem(newItem, oldItem, -1);
return true;
}
});
@@ -98,12 +93,24 @@ public abstract class ListModelEditorBase<T> extends CollectionModelEditor<T, Li
return items;
}
@Override
protected void silentlyReplaceItem(@NotNull T oldItem, @NotNull T newItem, int index) {
super.silentlyReplaceItem(oldItem, newItem, index);
model.checkSelectionOnSilentReplace(oldItem, newItem);
}
protected final class MyModel extends MutableCollectionComboBoxModel<T> {
@NotNull
final List<T> items() {
return super.getInternalList();
}
void checkSelectionOnSilentReplace(@NotNull T oldItem, @NotNull T newItem) {
if (mySelection == oldItem) {
mySelection = newItem;
}
}
@Override
protected void itemReplaced(@NotNull T existingItem, @Nullable T newItem) {
helper.remove(existingItem);
@@ -98,6 +98,7 @@ public class TableModelEditor<T> extends CollectionModelEditor<T, CollectionItem
}
}
@NotNull
public TableModelEditor<T> preferredScrollableViewportHeightInRows(int rows) {
table.setPreferredScrollableViewportSize(new Dimension(200, table.getRowHeight() * rows));
return this;
@@ -151,11 +152,13 @@ public class TableModelEditor<T> extends CollectionModelEditor<T, CollectionItem
}
}
@NotNull
public TableModelEditor<T> disableUpDownActions() {
toolbarDecorator.disableUpDownActions();
return this;
}
@NotNull
public TableModelEditor<T> enabled(boolean value) {
table.setEnabled(value);
return this;
@@ -365,12 +368,7 @@ public class TableModelEditor<T> extends CollectionModelEditor<T, CollectionItem
@Override
public boolean isEnabled(AnActionEvent e) {
for (T item : table.getSelectedObjects()) {
if (itemEditor.isRemovable(item)) {
return true;
}
}
return false;
return areSelectedItemsRemovable(table.getSelectionModel());
}
}
}
@@ -352,6 +352,7 @@ patch.apply.conflict.patched.version=Patched Version
patch.apply.select.title=Select Patch File
patch.apply.select.base.directory.title=Select Base Directory
shelve.changes.action=Shelve Changes
shelve.changes.progress.title=Shelving Changes...
patch.apply.already.applied=All of the changes in the specified patch are already contained in the code
patch.apply.partially.applied=Some of the changes in the specified patch were skipped because they are already contained in the code
patch.apply.success.applied.text=Patch successfully applied
@@ -105,12 +105,14 @@
<action id="EmmetNextEditPoint" class="com.intellij.codeInsight.template.emmet.actions.GoToEditPointAction$Forward"
text="Next Emmet Edit Point" description="Go to next Emmet edit point">
<keyboard-shortcut first-keystroke="alt shift CLOSE_BRACKET" keymap="$default"/>
<keyboard-shortcut first-keystroke="control alt RIGHT" keymap="Mac OS X" replace-all="true"/>
<keyboard-shortcut first-keystroke="control alt RIGHT" keymap="Mac OS X 10.5+" replace-all="true"/>
</action>
<action id="EmmetPreviousEditPoint" class="com.intellij.codeInsight.template.emmet.actions.GoToEditPointAction$Backward"
text="Previous Emmet Edit Point" description="Go to previous Emmet edit point">
<keyboard-shortcut first-keystroke="alt shift OPEN_BRACKET" keymap="$default"/>
<keyboard-shortcut first-keystroke="control alt LEFT" keymap="Mac OS X" replace-all="true"/>
<keyboard-shortcut first-keystroke="control alt LEFT" keymap="Mac OS X 10.5+" replace-all="true"/>
</action>
</group>
@@ -25,5 +25,6 @@
<orderEntry type="library" name="http-client" level="project" />
<orderEntry type="module" module-name="jps-model-impl" scope="TEST" />
<orderEntry type="library" name="gson" level="project" />
<orderEntry type="library" scope="TEST" name="KotlinJavaRuntime" level="project" />
</component>
</module>
@@ -0,0 +1 @@
<scheme name="1" data="true"/>
@@ -0,0 +1 @@
<scheme name="1" data="false"/>
@@ -0,0 +1 @@
<scheme name="1"/>
@@ -0,0 +1 @@
<scheme name="first"/>
@@ -0,0 +1 @@
<scheme name="second"/>
@@ -0,0 +1 @@
<scheme name="default"/>
@@ -1,288 +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.components.impl;
import com.intellij.application.options.PathMacrosImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.components.impl.stores.*;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.testFramework.LightPlatformTestCase;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.xmlb.XmlSerializerUtil;
import gnu.trove.THashMap;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
public class ApplicationStoreTest extends LightPlatformTestCase {
private File testAppConfig;
private MyComponentStore componentStore;
@Override
public void setUp() throws Exception {
super.setUp();
String testAppConfigPath = System.getProperty("test.app.config.path");
if (testAppConfigPath == null) {
testAppConfig = FileUtil.createTempDirectory("testAppSettings", null);
}
else {
testAppConfig = new File(FileUtil.expandUserHome(testAppConfigPath));
}
FileUtil.delete(testAppConfig);
componentStore = new MyComponentStore(testAppConfig.getAbsolutePath());
}
@Override
public void tearDown() throws Exception {
try {
Disposer.dispose(componentStore);
componentStore = null;
}
finally {
try {
super.tearDown();
}
finally {
FileUtil.delete(testAppConfig);
}
}
}
public void testStreamProviderSaveIfSeveralStoragesConfigured() throws Throwable {
SeveralStoragesConfigured component = new SeveralStoragesConfigured();
MyStreamProvider streamProvider = new MyStreamProvider();
componentStore.getStateStorageManager().setStreamProvider(streamProvider);
componentStore.initComponent(component, false);
component.foo = "newValue";
StoreUtil.save(componentStore, null);
assertThat(
streamProvider.data.get(RoamingType.PER_USER).get(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"),
equalTo("<application>\n" +
" <component name=\"HttpConfigurable\">\n" +
" <option name=\"foo\" value=\"newValue\" />\n" +
" </component>\n" +
"</application>"));
}
public void testLoadFromStreamProvider() throws Exception {
SeveralStoragesConfigured component = new SeveralStoragesConfigured();
MyStreamProvider streamProvider = new MyStreamProvider();
THashMap<String, String> map = new THashMap<String, String>();
map.put(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml", "<application>\n" +
" <component name=\"HttpConfigurable\">\n" +
" <option name=\"foo\" value=\"newValue\" />\n" +
" </component>\n" +
"</application>");
streamProvider.data.put(RoamingType.PER_USER, map);
componentStore.getStateStorageManager().setStreamProvider(streamProvider);
componentStore.initComponent(component, false);
assertThat(component.foo, equalTo("newValue"));
}
public void testRemoveDeprecatedStorageOnWrite() throws Exception {
doRemoveDeprecatedStorageOnWrite(new SeveralStoragesConfigured());
}
public void testRemoveDeprecatedStorageOnWrite2() throws Exception {
doRemoveDeprecatedStorageOnWrite(new ActualStorageLast());
}
private void doRemoveDeprecatedStorageOnWrite(@NotNull Foo component) throws IOException {
File oldFile = saveConfig("other.xml", "<application>" +
" <component name=\"HttpConfigurable\">\n" +
" <option name=\"foo\" value=\"old\" />\n" +
" </component>\n" +
"</application>");
saveConfig("proxy.settings.xml", "<application>\n" +
" <component name=\"HttpConfigurable\">\n" +
" <option name=\"foo\" value=\"new\" />\n" +
" </component>\n" +
"</application>");
componentStore.initComponent(component, false);
assertThat(component.foo, equalTo("new"));
component.foo = "new2";
StoreUtil.save(componentStore, null);
assertThat(oldFile.exists(), equalTo(false));
}
@NotNull
private File saveConfig(@NotNull String fileName, @Language("XML") String data) throws IOException {
File file = new File(testAppConfig, fileName);
FileUtil.writeToFile(file, data);
return file;
}
private static class MyStreamProvider extends StreamProvider {
public final Map<RoamingType, Map<String, String>> data = new THashMap<RoamingType, Map<String, String>>();
@Override
public void saveContent(@NotNull String fileSpec,
@NotNull byte[] content,
int size,
@NotNull RoamingType roamingType) {
getMap(roamingType).put(fileSpec, new String(content, 0, size, CharsetToolkit.UTF8_CHARSET));
}
private Map<String, String> getMap(@NotNull RoamingType roamingType) {
Map<String, String> map = data.get(roamingType);
if (map == null) {
map = new THashMap<String, String>();
data.put(roamingType, map);
}
return map;
}
@Nullable
@Override
public InputStream loadContent(@NotNull String fileSpec, @NotNull RoamingType roamingType) throws IOException {
String data = getMap(roamingType).get(fileSpec);
return data == null ? null : new ByteArrayInputStream(data.getBytes(CharsetToolkit.UTF8_CHARSET));
}
@Override
public void delete(@NotNull String fileSpec, @NotNull RoamingType roamingType) {
Map<String, String> map = data.get(roamingType);
if (map != null) {
map.remove(fileSpec);
}
}
}
static class MyComponentStore extends ComponentStoreImpl implements Disposable {
private final StateStorageManager stateStorageManager;
MyComponentStore(@NotNull final String testAppConfigPath) {
TrackingPathMacroSubstitutor macroSubstitutor = new ApplicationPathMacroManager().createTrackingSubstitutor();
stateStorageManager = new StateStorageManagerImpl(macroSubstitutor, "application", this, ApplicationManager.getApplication().getPicoContainer()) {
@NotNull
@Override
protected StorageData createStorageData(@NotNull String fileSpec, @NotNull String filePath) {
return new StorageData("application");
}
@Nullable
@Override
protected String getOldStorageSpec(@NotNull Object component, @NotNull String componentName, @NotNull StateStorageOperation operation) {
return null;
}
@Override
protected TrackingPathMacroSubstitutor getMacroSubstitutor(@NotNull final String fileSpec) {
if (fileSpec.equals(StoragePathMacros.APP_CONFIG + "/" + PathMacrosImpl.EXT_FILE_NAME + ".xml")) {
return null;
}
return super.getMacroSubstitutor(fileSpec);
}
};
stateStorageManager.addMacro(StoragePathMacros.APP_CONFIG, testAppConfigPath);
}
@Override
public void load() {
}
@NotNull
@Override
public StateStorageManager getStateStorageManager() {
return stateStorageManager;
}
@Override
public void dispose() {
}
@Nullable
@Override
protected PathMacroManager getPathMacroManagerForDefaults() {
return null;
}
@NotNull
@Override
protected MessageBus getMessageBus() {
return ApplicationManager.getApplication().getMessageBus();
}
}
abstract static class Foo {
public String foo = "defaultValue";
}
@State(
name = "HttpConfigurable",
storages = {
@Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"),
@Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true)
}
)
static class SeveralStoragesConfigured extends Foo implements PersistentStateComponent<SeveralStoragesConfigured> {
@Nullable
@Override
public SeveralStoragesConfigured getState() {
return this;
}
@Override
public void loadState(SeveralStoragesConfigured state) {
XmlSerializerUtil.copyBean(state, this);
}
}
@State(
name = "HttpConfigurable",
storages = {
@Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true),
@Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml")
}
)
static class ActualStorageLast extends Foo implements PersistentStateComponent<ActualStorageLast> {
@Nullable
@Override
public ActualStorageLast getState() {
return this;
}
@Override
public void loadState(ActualStorageLast state) {
XmlSerializerUtil.copyBean(state, this);
}
}
}
@@ -0,0 +1,225 @@
/*
* 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.components.impl
import com.intellij.application.options.PathMacrosImpl
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.*
import com.intellij.openapi.components.impl.stores.*
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.util.messages.MessageBus
import com.intellij.util.xmlb.XmlSerializerUtil
import gnu.trove.THashMap
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.intellij.lang.annotations.Language
import java.io.ByteArrayInputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
public class ApplicationStoreTest : LightPlatformTestCase() {
private var testAppConfig: File? = null
private var componentStore: MyComponentStore? = null
throws(Exception::class)
override fun setUp() {
super.setUp()
val testAppConfigPath = System.getProperty("test.app.config.path")
if (testAppConfigPath == null) {
testAppConfig = FileUtil.createTempDirectory("testAppSettings", null)
}
else {
testAppConfig = File(FileUtil.expandUserHome(testAppConfigPath))
}
FileUtil.delete(testAppConfig!!)
componentStore = MyComponentStore(testAppConfig!!.getAbsolutePath())
}
throws(Exception::class)
override fun tearDown() {
try {
Disposer.dispose(componentStore!!)
componentStore = null
}
finally {
try {
super.tearDown()
}
finally {
FileUtil.delete(testAppConfig!!)
}
}
}
public fun testStreamProviderSaveIfSeveralStoragesConfigured() {
val component = SeveralStoragesConfigured()
val streamProvider = MyStreamProvider()
componentStore!!.getStateStorageManager().setStreamProvider(streamProvider)
componentStore!!.initComponent(component, false)
component.foo = "newValue"
StoreUtil.save(componentStore!!, null)
assertThat<String>(streamProvider.data.get(RoamingType.PER_USER)!!.get(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"), equalTo("<application>\n" + " <component name=\"HttpConfigurable\">\n" + " <option name=\"foo\" value=\"newValue\" />\n" + " </component>\n" + "</application>"))
}
public fun testLoadFromStreamProvider() {
val component = SeveralStoragesConfigured()
val streamProvider = MyStreamProvider()
val map = THashMap<String, String>()
map.put(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml", "<application>\n" + " <component name=\"HttpConfigurable\">\n" + " <option name=\"foo\" value=\"newValue\" />\n" + " </component>\n" + "</application>")
streamProvider.data.put(RoamingType.PER_USER, map)
componentStore!!.getStateStorageManager().setStreamProvider(streamProvider)
componentStore!!.initComponent(component, false)
assertThat(component.foo, equalTo("newValue"))
}
public fun testRemoveDeprecatedStorageOnWrite() {
}
public fun testRemoveDeprecatedStorageOnWrite2() {
doRemoveDeprecatedStorageOnWrite(ActualStorageLast())
}
private fun doRemoveDeprecatedStorageOnWrite(component: Foo) {
val oldFile = saveConfig("other.xml", "<application>" + " <component name=\"HttpConfigurable\">\n" + " <option name=\"foo\" value=\"old\" />\n" + " </component>\n" + "</application>")
saveConfig("proxy.settings.xml", "<application>\n" + " <component name=\"HttpConfigurable\">\n" + " <option name=\"foo\" value=\"new\" />\n" + " </component>\n" + "</application>")
componentStore!!.initComponent(component, false)
assertThat(component.foo, equalTo("new"))
component.foo = "new2"
StoreUtil.save(componentStore!!, null)
assertThat(oldFile.exists(), equalTo(false))
}
throws(IOException::class)
private fun saveConfig(fileName: String, Language("XML") data: String): File {
val file = File(testAppConfig, fileName)
FileUtil.writeToFile(file, data)
return file
}
private class MyStreamProvider : StreamProvider {
public val data: MutableMap<RoamingType, MutableMap<String, String>> = THashMap()
override fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) {
getMap(roamingType).put(fileSpec, String(content, 0, size, CharsetToolkit.UTF8_CHARSET))
}
private fun getMap(roamingType: RoamingType): MutableMap<String, String> {
var map = data.get(roamingType)
if (map == null) {
map = THashMap<String, String>()
data.put(roamingType, map)
}
return map
}
throws(IOException::class)
override fun loadContent(fileSpec: String, roamingType: RoamingType): InputStream? {
val data = getMap(roamingType).get(fileSpec)
return if (data == null) null else ByteArrayInputStream(data.toByteArray(CharsetToolkit.UTF8_CHARSET))
}
override fun delete(fileSpec: String, roamingType: RoamingType) {
val map = data.get(roamingType)
map?.remove(fileSpec)
}
}
class MyComponentStore(testAppConfigPath: String) : ComponentStoreImpl(), Disposable {
private val stateStorageManager: StateStorageManager
init {
val macroSubstitutor = ApplicationPathMacroManager().createTrackingSubstitutor()
stateStorageManager = object : StateStorageManagerImpl(macroSubstitutor, "application", this, ApplicationManager.getApplication().getPicoContainer()) {
override fun createStorageData(fileSpec: String, filePath: String): StorageData {
return StorageData("application")
}
override fun getOldStorageSpec(component: Any, componentName: String, operation: StateStorageOperation): String? {
return null
}
override fun getMacroSubstitutor(fileSpec: String): TrackingPathMacroSubstitutor? {
if (fileSpec == StoragePathMacros.APP_CONFIG + "/" + PathMacrosImpl.EXT_FILE_NAME + ".xml") {
return null
}
return super.getMacroSubstitutor(fileSpec)
}
}
stateStorageManager.addMacro(StoragePathMacros.APP_CONFIG, testAppConfigPath)
}
override fun load() {
}
override fun getStateStorageManager(): StateStorageManager {
return stateStorageManager
}
override fun dispose() {
}
override fun getPathMacroManagerForDefaults(): PathMacroManager? {
return null
}
override fun getMessageBus(): MessageBus {
return ApplicationManager.getApplication().getMessageBus()
}
}
abstract class Foo {
public var foo: String = "defaultValue"
}
State(name = "HttpConfigurable", storages = arrayOf(Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"), Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true)))
class SeveralStoragesConfigured : Foo(), PersistentStateComponent<SeveralStoragesConfigured> {
override fun getState(): SeveralStoragesConfigured? {
return this
}
override fun loadState(state: SeveralStoragesConfigured) {
XmlSerializerUtil.copyBean(state, this)
}
}
State(name = "HttpConfigurable", storages = arrayOf(Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true), Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml")))
class ActualStorageLast : Foo(), PersistentStateComponent<ActualStorageLast> {
override fun getState(): ActualStorageLast? {
return this
}
override fun loadState(state: ActualStorageLast) {
XmlSerializerUtil.copyBean(state, this)
}
}
}
@@ -0,0 +1,40 @@
package com.intellij.options
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.impl.stores.StreamProvider
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.SmartList
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
class MockStreamProvider(private val myBaseDir: File) : StreamProvider {
override fun isApplicable(fileSpec: String, roamingType: RoamingType) = roamingType === RoamingType.PER_USER
override fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) {
FileUtil.writeToFile(File(myBaseDir, fileSpec), content, 0, size)
}
override fun loadContent(fileSpec: String, roamingType: RoamingType): InputStream? {
val file = File(myBaseDir, fileSpec)
//noinspection IOResourceOpenedButNotSafelyClosed
return if (file.exists()) FileInputStream(file) else null
}
override fun listSubFiles(fileSpec: String, roamingType: RoamingType): Collection<String> {
if (roamingType !== RoamingType.PER_USER) {
return emptyList()
}
val files = File(myBaseDir, fileSpec).listFiles() ?: return emptyList()
val names = SmartList<String>()
for (file in files) {
names.add(file.getName())
}
return names
}
override fun delete(fileSpec: String, roamingType: RoamingType) {
FileUtil.delete(File(myBaseDir, fileSpec))
}
}
@@ -0,0 +1,368 @@
package com.intellij.options
import com.intellij.openapi.application.invokeAndWaitIfNeed
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.options.BaseSchemeProcessor
import com.intellij.openapi.options.ExternalizableScheme
import com.intellij.openapi.options.SchemeManagerImpl
import com.intellij.openapi.util.JDOMUtil
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.util.SmartList
import com.intellij.util.lang.CompoundRuntimeException
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters
import com.intellij.util.xmlb.XmlSerializer
import com.intellij.util.xmlb.annotations.Attribute
import com.intellij.util.xmlb.annotations.Tag
import com.intellij.util.xmlb.annotations.Transient
import gnu.trove.THashMap
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.CoreMatchers.not
import org.hamcrest.CoreMatchers.notNullValue
import org.hamcrest.CoreMatchers.sameInstance
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.collection.IsMapContaining.hasKey
import org.jdom.Element
import org.jetbrains.testFramework.FixtureRule
import org.jetbrains.testFramework.TemporaryDirectory
import org.jetbrains.testFramework.exists
import org.junit.Rule
import org.junit.Test
import java.io.File
val FILE_SPEC = "REMOTE"
class SchemeManagerTest {
private val fixtureManager = FixtureRule()
public Rule fun getFixtureManager(): FixtureRule = fixtureManager
private val tempDirManager = TemporaryDirectory()
public Rule fun getTemporaryFolder(): TemporaryDirectory = tempDirManager
private var localBaseDir: File? = null
private var remoteBaseDir: File? = null
private fun getTestDataPath() = PlatformTestUtil.getCommunityPath().replace(File.separatorChar, '/') + "/platform/platform-tests/testData/options"
public Test fun testLoadSchemes() {
doLoadSaveTest("options1", "1->first;2->second")
}
public Test fun testLoadSimpleSchemes() {
doLoadSaveTest("options", "1->1")
}
public Test fun testDeleteScheme() {
val manager = createAndLoad("options1")
manager.removeScheme(TestScheme("first"))
manager.save()
checkSchemes("2->second")
}
public Test fun testRenameScheme() {
val manager = createAndLoad("options1")
val scheme = manager.findSchemeByName("first")
assertThat(scheme, notNullValue())
assert(scheme != null)
scheme!!.setName("renamed")
manager.save()
checkSchemes("2->second;renamed->renamed")
}
public Test fun testRenameScheme2() {
val manager = createAndLoad("options1")
val first = manager.findSchemeByName("first")
assertThat(first, notNullValue())
assert(first != null)
first!!.setName("2")
val second = manager.findSchemeByName("second")
assertThat(second, notNullValue())
assert(second != null)
second!!.setName("1")
manager.save()
checkSchemes("1->1;2->2")
}
public Test fun testDeleteRenamedScheme() {
val manager = createAndLoad("options1")
val firstScheme = manager.findSchemeByName("first")
assertThat(firstScheme, notNullValue())
assert(firstScheme != null)
firstScheme!!.setName("first_renamed")
manager.save()
checkSchemes(File(remoteBaseDir, "REMOTE"), "first_renamed->first_renamed;2->second", true)
checkSchemes(localBaseDir!!, "", false)
firstScheme.setName("first_renamed2")
manager.removeScheme(firstScheme)
manager.save()
checkSchemes(File(remoteBaseDir, "REMOTE"), "2->second", true)
checkSchemes(localBaseDir!!, "", false)
}
public Test fun testDeleteAndCreateSchemeWithTheSameName() {
val manager = createAndLoad("options1")
val firstScheme = manager.findSchemeByName("first")
assertThat(firstScheme, notNullValue())
manager.removeScheme(firstScheme!!)
manager.addScheme(TestScheme("first"))
manager.save()
checkSchemes("2->second;first->first")
}
public Test fun testGenerateUniqueSchemeName() {
val manager = createAndLoad("options1")
val scheme = TestScheme("first")
manager.addNewScheme(scheme, false)
assertThat("first2", equalTo(scheme.getName()))
}
public Test fun `different extensions`() {
val dir = tempDirManager.newDirectory()
FileUtil.copyDir(File("${getTestDataPath()}/different-extensions"), dir, false)
val schemesManager = SchemeManagerImpl<TestScheme, TestScheme>(FILE_SPEC, object: TestSchemesProcessor() {
override fun isUpgradeNeeded() = true
override fun getSchemeExtension() = ".icls"
}, RoamingType.PER_USER, null, dir)
schemesManager.loadSchemes()
val schemes = schemesManager.getAllSchemes()
assertThat(schemes.size(), equalTo(1))
val scheme = schemes.get(0)
assertThat(scheme.data, equalTo("true"))
assertThat(File(dir, "1.icls"), exists())
assertThat(File(dir, "1.xml"), exists())
scheme.data = "newTrue"
save(schemesManager)
assertThat(File(dir, "1.icls"), exists())
assertThat(File(dir, "1.xml"), not(exists()))
}
public Test fun setSchemes() {
val dir = tempDirManager.newDirectory()
val schemesManager = createSchemeManager(dir)
schemesManager.loadSchemes()
assertThat(schemesManager.getAllSchemes().isEmpty(), equalTo(true))
val scheme = TestScheme("s1")
schemesManager.setSchemes(listOf(scheme))
val schemes = schemesManager.getAllSchemes()
assertThat(schemes.size(), equalTo(1))
assertThat(schemes.get(0), sameInstance(scheme))
assertThat(File(dir, "s1.xml"), not(exists()))
scheme.data = "newTrue"
save(schemesManager)
assertThat(File(dir, "s1.xml"), exists())
schemesManager.setSchemes(emptyList())
save(schemesManager)
assertThat(dir, not(exists()))
}
public Test fun `save only if scheme differs from bundled`() {
val dir = tempDirManager.newDirectory()
var schemeManager = createSchemeManager(dir)
val converter: (Element) -> TestScheme = { XmlSerializer.deserialize(it, javaClass<TestScheme>())!! }
val bundledPath = "/bundledSchemes/default"
schemeManager.loadBundledScheme(bundledPath, this, converter)
var schemes = schemeManager.getAllSchemes()
assertThat(schemes.size(), equalTo(1))
val customScheme = TestScheme("default")
assertThat(schemes.get(0), equalTo(customScheme))
save(schemeManager)
assertThat(dir, not(exists()))
save(schemeManager)
schemeManager.setSchemes(listOf(customScheme))
assertThat(dir, not(exists()))
schemes = schemeManager.getAllSchemes()
assertThat(schemes.size(), equalTo(1))
assertThat(schemes.get(0), sameInstance(customScheme))
customScheme.data = "foo"
save(schemeManager)
val schemeFile = File(dir, "default.xml")
assertThat(schemeFile, exists())
schemeManager = createSchemeManager(dir)
schemeManager.loadBundledScheme(bundledPath, this, converter)
schemeManager.loadSchemes()
schemes = schemeManager.getAllSchemes()
assertThat(schemes.get(0), equalTo(customScheme))
}
private fun createSchemeManager(dir: File) = SchemeManagerImpl<TestScheme, TestScheme>(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, null, dir)
public Test fun `don't remove dir if no schemes but at least one non-hidden file exists`() {
val dir = tempDirManager.newDirectory()
val schemesManager = SchemeManagerImpl<TestScheme, TestScheme>(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, null, dir)
val scheme = TestScheme("s1")
schemesManager.setSchemes(listOf(scheme))
save(schemesManager)
val schemeFile = File(dir, "s1.xml")
assertThat(schemeFile.exists(), equalTo(true))
schemesManager.setSchemes(emptyList())
FileUtil.writeToFile(File(dir, "empty"), byteArrayOf())
save(schemesManager)
assertThat(schemeFile.exists(), equalTo(false))
assertThat(dir.exists(), equalTo(true))
}
public Test fun rename() {
val dir = tempDirManager.newDirectory()
val schemesManager = SchemeManagerImpl<TestScheme, TestScheme>(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, null, dir)
schemesManager.loadSchemes()
assertThat(schemesManager.getAllSchemes().isEmpty(), equalTo(true))
val scheme = TestScheme("s1")
schemesManager.setSchemes(listOf(scheme))
val schemes = schemesManager.getAllSchemes()
assertThat(schemes.size(), equalTo(1))
assertThat(schemes.get(0), sameInstance(scheme))
assertThat(File(dir, "s1.xml").exists(), equalTo(false))
scheme.data = "newTrue"
save(schemesManager)
assertThat(File(dir, "s1.xml").exists(), equalTo(true))
scheme.setName("s2")
save(schemesManager)
assertThat(File(dir, "s1.xml").exists(), equalTo(false))
assertThat(File(dir, "s2.xml").exists(), equalTo(true))
}
private fun save(schemeManager: SchemeManagerImpl<TestScheme, TestScheme>) {
invokeAndWaitIfNeed {
schemeManager.save()
}
}
private fun createAndLoad(testData: String): SchemeManagerImpl<TestScheme, TestScheme> {
createTempFiles(testData)
return createAndLoad()
}
private fun doLoadSaveTest(testData: String, expected: String, localExpected: String = "") {
val schemesManager = createAndLoad(testData)
schemesManager.save()
checkSchemes(File(remoteBaseDir, "REMOTE"), expected, true)
checkSchemes(localBaseDir!!, localExpected, false)
}
private fun checkSchemes(expected: String) {
checkSchemes(File(remoteBaseDir, "REMOTE"), expected, true)
checkSchemes(localBaseDir!!, "", false)
}
private fun createAndLoad(): SchemeManagerImpl<TestScheme, TestScheme> {
val schemesManager = SchemeManagerImpl<TestScheme, TestScheme>(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, MockStreamProvider(remoteBaseDir!!), localBaseDir!!)
schemesManager.loadSchemes()
return schemesManager
}
private fun createTempFiles(testData: String) {
val temp = tempDirManager.newDirectory()
localBaseDir = File(temp, "__local")
remoteBaseDir = temp
FileUtil.copyDir(File("${getTestDataPath()}/$testData"), File(temp, "REMOTE"))
}
}
private fun checkSchemes(baseDir: File, expected: String, ignoreDeleted: Boolean) {
val filesToScheme = StringUtil.split(expected, ";")
val fileToSchemeMap = THashMap<String, String>()
for (fileToScheme in filesToScheme) {
val index = fileToScheme.indexOf("->")
fileToSchemeMap.put(fileToScheme.substring(0, index), fileToScheme.substring(index + 2))
}
val files = baseDir.listFiles()
if (files != null) {
for (file in files) {
val fileName = FileUtil.getNameWithoutExtension(file)
if ("--deleted" == fileName && ignoreDeleted) {
assertThat<Map<String, String>>(fileToSchemeMap, hasKey(fileName))
}
}
}
for (file in fileToSchemeMap.keySet()) {
assertThat(File(baseDir, "$file.xml").isFile(), equalTo(true))
}
if (files != null) {
val schemesProcessor = TestSchemesProcessor()
for (file in files) {
val fileName = FileUtil.getNameWithoutExtension(file)
val scheme = schemesProcessor.readScheme(JDOMUtil.load(file), true)
assertThat(fileToSchemeMap.get(fileName), equalTo(scheme!!.getName()))
}
}
}
public data Tag("scheme") class TestScheme(Attribute private var name: String = "", Attribute var data: String? = null) : ExternalizableScheme {
override fun getName() = name
override Transient fun setName(newName: String) {
name = newName
}
@suppress("DEPRECATED_SYMBOL_WITH_MESSAGE")
override fun getExternalInfo() = null
}
public open class TestSchemesProcessor : BaseSchemeProcessor<TestScheme>() {
override fun readScheme(element: Element) = XmlSerializer.deserialize(element, javaClass<TestScheme>())
override fun writeScheme(scheme: TestScheme) = scheme.serialize()
}
fun SchemeManagerImpl<*, *>.save() {
val errors = SmartList<Throwable>()
save(errors)
CompoundRuntimeException.doThrow(errors)
}
public fun <T : Any> T.serialize(): Element = XmlSerializer.serialize(this, SkipDefaultValuesSerializationFilters())
public fun Element.toByteArray(): ByteArray {
val out = BufferExposingByteArrayOutputStream(512)
JDOMUtil.writeParent(this, out, "\n")
return out.toByteArray()
}
@@ -88,7 +88,7 @@ class Generator(outputDir: String, rootPackage: String, requestClassName: String
}
fun resolveType(typedObject: ItemDescriptor, scope: ResolveAndGenerateScope): TypeDescriptor {
val optional = typedObject is ItemDescriptor.Named && (typedObject : ItemDescriptor.Named).optional()
val optional = typedObject is ItemDescriptor.Named && typedObject.optional()
return switchByType(typedObject, object : TypeVisitor<TypeDescriptor> {
override fun visitRef(refName: String): TypeDescriptor {
return TypeDescriptor(resolveRefType(scope.getDomainName(), refName, scope.getTypeDirection()), optional)
@@ -120,7 +120,7 @@ class Generator(outputDir: String, rootPackage: String, requestClassName: String
}
override fun visitArray(items: ProtocolMetaModel.ArrayItemType): TypeDescriptor {
val type = scope.resolveType<ProtocolMetaModel.ArrayItemType>(items).type
val type = scope.resolveType(items).type
return TypeDescriptor(ListType(type), optional, false, type == BoxableType.ANY_STRING)
}
@@ -244,7 +244,7 @@ fun capitalizeFirstChar(s: String): String {
}
fun <R> switchByType(typedObject: ItemDescriptor, visitor: TypeVisitor<R>): R {
val refName = if (typedObject is ItemDescriptor.Referenceable) (typedObject : ItemDescriptor.Referenceable).ref() else null
val refName = if (typedObject is ItemDescriptor.Referenceable) typedObject.ref() else null
if (refName != null) {
return visitor.visitRef(refName)
}
@@ -265,7 +265,7 @@ fun <R> switchByType(typedObject: ItemDescriptor, visitor: TypeVisitor<R>): R {
return visitor.visitObject(null)
}
val properties = (typedObject : ItemDescriptor.Type).properties()
val properties = typedObject.properties()
if (properties == null || properties.isEmpty()) {
return visitor.visitMap()
}
@@ -7,7 +7,7 @@ data class NamePath(val lastComponent: String, val parent: NamePath? = null) {
var current: NamePath? = this
while (current != null) {
res++
current = current!!.parent
current = current.parent
}
}
return res
@@ -15,10 +15,7 @@ class TypeMap {
private val typesToGenerate = ArrayList<StandaloneTypeBinding>()
fun resolve(domainName: String, typeName: String, direction: TypeData.Direction): BoxableType? {
val domainGenerator = domainGeneratorMap!!.get(domainName)
if (domainGenerator == null) {
throw RuntimeException("Failed to find domain generator: " + domainName)
}
val domainGenerator = domainGeneratorMap!!.get(domainName) ?: throw RuntimeException("Failed to find domain generator: " + domainName)
return direction.get(getTypeData(domainName, typeName)).resolve(this, domainGenerator)
}
@@ -28,7 +25,7 @@ class TypeMap {
fun generateRequestedTypes() {
// size may grow during iteration
var list = typesToGenerate.copyToArray()
var list = typesToGenerate.toTypedArray()
typesToGenerate.clear()
while (true) {
for (binding in list) {
@@ -39,7 +36,7 @@ class TypeMap {
break
}
else {
list = typesToGenerate.copyToArray()
list = typesToGenerate.toTypedArray()
typesToGenerate.clear()
}
}
@@ -52,6 +49,6 @@ class TypeMap {
result = TypeData(typeName)
map.put(key, result)
}
return result!!
return result
}
}
@@ -28,11 +28,13 @@ import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties
import com.intellij.execution.testframework.sm.runner.SMTestLocator;
import com.intellij.execution.testframework.sm.runner.TestProxyFilterProvider;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.project.Project;
import com.intellij.pom.Navigatable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
public class ImportedTestConsoleProperties extends SMTRunnerConsoleProperties implements SMCustomMessagesParsing {
@@ -111,4 +113,11 @@ public class ImportedTestConsoleProperties extends SMTRunnerConsoleProperties im
public AbstractRerunFailedTestsAction createRerunFailedTestsAction(ConsoleView consoleView) {
return myProperties == null ? null : myProperties.createRerunFailedTestsAction(consoleView);
}
@Override
public void appendAdditionalActions(DefaultActionGroup actionGroup, JComponent parent, TestConsoleProperties target) {
if (myProperties != null) {
myProperties.appendAdditionalActions(actionGroup, parent, this);
}
}
}
@@ -52,7 +52,12 @@ public class ImportedTestContentHandler extends DefaultHandler {
myCurrentTest = name;
myDuration = attributes.getValue(TestResultsXmlFormatter.ATTR_DURATION);
myStatus = attributes.getValue(TestResultsXmlFormatter.ATTR_STATUS);
myProcessor.onTestStarted(new TestStartedEvent(name, attributes.getValue(TestResultsXmlFormatter.ATTR_LOCATION)));
final String isConfig = attributes.getValue(TestResultsXmlFormatter.ATTR_CONFIG);
final TestStartedEvent startedEvent = new TestStartedEvent(name, attributes.getValue(TestResultsXmlFormatter.ATTR_LOCATION));
if (isConfig != null && Boolean.valueOf(isConfig)) {
startedEvent.setConfig(true);
}
myProcessor.onTestStarted(startedEvent);
currentValue.setLength(0);
}
else if (TestResultsXmlFormatter.ELEM_OUTPUT.equals(qName)) {
@@ -18,6 +18,7 @@ package com.intellij.execution.testframework.sm.runner.history.actions;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.ExecutorRegistry;
import com.intellij.execution.RunnerRegistry;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
@@ -26,6 +27,7 @@ import com.intellij.execution.impl.RunManagerImpl;
import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ExecutionEnvironmentBuilder;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.testframework.export.TestResultsXmlFormatter;
import com.intellij.execution.testframework.sm.runner.history.ImportedTestRunnableState;
import com.intellij.execution.testframework.sm.runner.SMRunnerConsolePropertiesProvider;
@@ -115,7 +117,14 @@ public abstract class AbstractImportTestsAction extends AnAction {
}
final Executor executor = properties != null ? properties.getExecutor()
: ExecutorRegistry.getInstance().getExecutorById(DefaultRunExecutor.EXECUTOR_ID);
ExecutionEnvironmentBuilder.create(project, executor, profile).buildAndExecute();
ExecutionEnvironmentBuilder builder = ExecutionEnvironmentBuilder.create(project, executor, profile);
final RunConfiguration initialConfiguration = profile.getInitialConfiguration();
final ProgramRunner runner =
initialConfiguration != null ? RunnerRegistry.getInstance().getRunner(executor.getId(), initialConfiguration) : null;
if (runner != null) {
builder = builder.runner(runner);
}
builder.buildAndExecute();
}
catch (ExecutionException e1) {
Messages.showErrorDialog(project, e1.getMessage(), "Import Failed");
@@ -214,6 +223,10 @@ public abstract class AbstractImportTestsAction extends AnAction {
return myProperties;
}
public RunConfiguration getInitialConfiguration() {
return mySettings != null ? mySettings.getConfiguration() : null;
}
public Project getProject() {
return myProject;
}
@@ -319,6 +319,10 @@ public class SMTestRunnerResultsForm extends TestResultsPanel
}
fireOnTestingFinished();
if (testsRoot.wasTerminated() && myStatusLine.getStatusColor() == ColorProgressBar.GREEN) {
myStatusLine.setStatusColor(JBColor.LIGHT_GRAY);
}
if (testsRoot.isEmptySuite() &&
testsRoot.isTestsReporterAttached() &&
@@ -773,12 +777,14 @@ public class SMTestRunnerResultsForm extends TestResultsPanel
private static class MySaveHistoryTask extends Task.Backgroundable {
private final TestConsoleProperties myConsoleProperties;
private SMTestProxy.SMRootTestProxy myRoot;
private RunConfiguration myConfiguration;
private String myOutput;
public MySaveHistoryTask(TestConsoleProperties consoleProperties, SMTestProxy.SMRootTestProxy root, RunConfiguration configuration) {
super(consoleProperties.getProject(), "Save Test Results", true);
myConsoleProperties = consoleProperties;
myRoot = root;
myConfiguration = configuration;
}
@@ -796,7 +802,7 @@ public class SMTestRunnerResultsForm extends TestResultsPanel
final SMTestProxy.SMRootTestProxy root = myRoot;
final RunConfiguration configuration = myConfiguration;
if (root != null && configuration != null) {
TestResultsXmlFormatter.execute(root, configuration, handler);
TestResultsXmlFormatter.execute(root, configuration, myConsoleProperties, handler);
}
myOutput = w.toString();
}
+2 -17
View File
@@ -13,27 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.ide
package org.jetbrains.testFramework
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.application.invokeAndWaitIfNeed
import com.intellij.testFramework.UsefulTestCase
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory
import org.junit.rules.ExternalResource
import javax.swing.SwingUtilities
public fun invokeAndWaitIfNeed(runnable: () -> Unit) {
if (SwingUtilities.isEventDispatchThread()) runnable() else SwingUtilities.invokeAndWait(runnable)
}
public inline fun runWriteAction(runnable: () -> Unit) {
val token = WriteAction.start()
try {
runnable()
}
finally {
token.finish()
}
}
public class FixtureRule() : ExternalResource() {
val projectFixture = IdeaTestFixtureFactory.getFixtureFactory().createLightFixtureBuilder().getFixture()
@@ -0,0 +1,39 @@
/*
* 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 org.jetbrains.testFramework
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.util.SmartList
import org.junit.rules.ExternalResource
import java.io.File
public class TemporaryDirectory : ExternalResource() {
private val files = SmartList<File>()
override fun after() {
for (file in files) {
FileUtil.delete(file)
}
files.clear()
}
public fun newDirectory(): File {
val file = FileUtilRt.generateRandomTemporaryPath()
files.add(file)
return file;
}
}
@@ -0,0 +1,39 @@
/*
* 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 org.jetbrains.testFramework
import org.hamcrest.Description
import org.hamcrest.Factory
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeDiagnosingMatcher
import java.io.File
public class FileExistenceMatcher(private val exists: Boolean) : TypeSafeDiagnosingMatcher<File>(javaClass<File>()) {
override fun matchesSafely(file: File, mismatchDescription: Description): Boolean {
if (exists == file.exists()) {
return true
}
mismatchDescription.appendText("is not a file")
return false
}
override fun describeTo(description: Description) {
description.appendText("an existing file")
}
}
public Factory fun exists(): Matcher<File> = FileExistenceMatcher(true)
@@ -127,10 +127,18 @@ public abstract class Filter<T extends AbstractTestProxy> {
}
});
public static final Filter SUCCESSFUL_CONFIGS = new Filter() {
public static final Filter HIDE_SUCCESSFUL_CONFIGS = new Filter() {
@Override
public boolean shouldAccept(AbstractTestProxy test) {
return !test.isConfig() || !test.isPassed();
final List<? extends AbstractTestProxy> children = test.getChildren();
if (!children.isEmpty()) {
for (AbstractTestProxy proxy : children) {
if (!proxy.isConfig() || !proxy.isPassed()) return true;
}
return false;
}
return !(test.isConfig() && test.isPassed());
}
};
@@ -195,7 +195,7 @@ public abstract class TestConsoleProperties extends StoringPropertyContainer imp
myUsePredefinedMessageFilter = usePredefinedMessageFilter;
}
protected void appendAdditionalActions(DefaultActionGroup actionGroup, JComponent parent) { }
public void appendAdditionalActions(DefaultActionGroup actionGroup, JComponent parent, TestConsoleProperties target) { }
@Nullable
protected AnAction createImportAction() {
@@ -203,16 +203,16 @@ public abstract class TestConsoleProperties extends StoringPropertyContainer imp
}
@NotNull
protected ToggleBooleanProperty createIncludeNonStartedInRerun() {
protected ToggleBooleanProperty createIncludeNonStartedInRerun(TestConsoleProperties target) {
String text = ExecutionBundle.message("junit.runing.info.include.non.started.in.rerun.failed.action.name");
return new ToggleBooleanProperty(text, null, null, this, INCLUDE_NON_STARTED_IN_RERUN_FAILED);
return new ToggleBooleanProperty(text, null, null, target, INCLUDE_NON_STARTED_IN_RERUN_FAILED);
}
@NotNull
protected ToggleBooleanProperty createHideSuccessfulConfig() {
protected ToggleBooleanProperty createHideSuccessfulConfig(TestConsoleProperties target) {
String text = ExecutionBundle.message("junit.runing.info.hide.successful.config.action.name");
setIfUndefined(HIDE_SUCCESSFUL_CONFIG, true);
return new ToggleBooleanProperty(text, null, null, this, HIDE_SUCCESSFUL_CONFIG);
return new ToggleBooleanProperty(text, null, null, target, HIDE_SUCCESSFUL_CONFIG);
}
@JdkConstants.TreeSelectionMode
@@ -23,7 +23,6 @@ package com.intellij.execution.testframework;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.testframework.actions.ScrollToTestSourceAction;
import com.intellij.execution.testframework.actions.ShowStatisticsAction;
import com.intellij.execution.testframework.actions.TestFrameworkActions;
@@ -155,7 +154,7 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab
secondaryGroup.addSeparator();
secondaryGroup.add(new ToggleBooleanProperty(ExecutionBundle.message("junit.runing.info.select.first.failed.action.name"),
null, null, properties, TestConsoleProperties.SELECT_FIRST_DEFECT));
properties.appendAdditionalActions(secondaryGroup, parent);
properties.appendAdditionalActions(secondaryGroup, parent, properties);
actionGroup.add(secondaryGroup);
add(ActionManager.getInstance().
@@ -56,7 +56,7 @@ public class TestFrameworkActions {
}
final boolean hideSuccessfulConfigs = TestConsoleProperties.HIDE_SUCCESSFUL_CONFIG.value(properties);
final Filter hideConfigsFilter = hideSuccessfulConfigs ? Filter.SUCCESSFUL_CONFIGS : Filter.NO_FILTER;
final Filter hideConfigsFilter = hideSuccessfulConfigs ? Filter.HIDE_SUCCESSFUL_CONFIGS : Filter.NO_FILTER;
return hidePassedFilter.and(hideIgnoredFilter).and(hideConfigsFilter);
}
@@ -307,7 +307,7 @@ public class ExportTestResultsAction extends DumbAwareAction {
StringWriter w = new StringWriter();
handler.setResult(new StreamResult(w));
try {
TestResultsXmlFormatter.execute(myModel.getRoot(), myRunConfiguration, handler);
TestResultsXmlFormatter.execute(myModel.getRoot(), myRunConfiguration, myModel.getProperties(), handler);
}
catch (ProcessCanceledException e) {
return null;
@@ -18,12 +18,10 @@ package com.intellij.execution.testframework.export;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.filters.*;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.impl.RunManagerImpl;
import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl;
import com.intellij.execution.testframework.AbstractTestProxy;
import com.intellij.execution.testframework.Printable;
import com.intellij.execution.testframework.Printer;
import com.intellij.execution.testframework.TestProxyRoot;
import com.intellij.execution.testframework.*;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.extensions.Extensions;
@@ -59,6 +57,7 @@ public class TestResultsXmlFormatter {
public static final String ATTR_STATUS = "status";
public static final String TOTAL_STATUS = "total";
private static final String ATTR_FOORTER_TEXT = "footerText";
public static final String ATTR_CONFIG = "isConfig";
public static final String STATUS_PASSED = "passed";
public static final String STATUS_FAILED = "failed";
public static final String STATUS_ERROR = "error";
@@ -72,16 +71,21 @@ public class TestResultsXmlFormatter {
private final RunConfiguration myRuntimeConfiguration;
private final ContentHandler myResultHandler;
private final AbstractTestProxy myTestRoot;
private final boolean myHidePassedConfig;
public static void execute(AbstractTestProxy root, RunConfiguration runtimeConfiguration, ContentHandler resultHandler)
public static void execute(AbstractTestProxy root, RunConfiguration runtimeConfiguration, TestConsoleProperties properties, ContentHandler resultHandler)
throws SAXException {
new TestResultsXmlFormatter(root, runtimeConfiguration, resultHandler).execute();
new TestResultsXmlFormatter(root, runtimeConfiguration, properties, resultHandler).execute();
}
private TestResultsXmlFormatter(AbstractTestProxy root, RunConfiguration runtimeConfiguration, ContentHandler resultHandler) {
private TestResultsXmlFormatter(AbstractTestProxy root,
RunConfiguration runtimeConfiguration,
TestConsoleProperties properties,
ContentHandler resultHandler) {
myRuntimeConfiguration = runtimeConfiguration;
myTestRoot = root;
myResultHandler = resultHandler;
myHidePassedConfig = TestConsoleProperties.HIDE_SUCCESSFUL_CONFIG.value(properties);
}
private void execute() throws SAXException {
@@ -205,6 +209,9 @@ public class TestResultsXmlFormatter {
if (locationUrl != null) {
attrs.put(ATTR_LOCATION, locationUrl);
}
if (node.isConfig()) {
attrs.put(ATTR_CONFIG, "true");
}
String elemName = node.isLeaf() ? ELEM_TEST : ELEM_SUITE;
startElement(elemName, attrs);
if (node.isLeaf()) {
@@ -250,7 +257,7 @@ public class TestResultsXmlFormatter {
}
else {
for (AbstractTestProxy child : node.getChildren()) {
if (child.isConfig() && child.isPassed()) {
if (myHidePassedConfig && child.isConfig() && child.isPassed()) {
//ignore configurations during export
continue;
}
@@ -18,14 +18,40 @@ package com.intellij.usages.actions;
import com.intellij.usages.Usage;
import com.intellij.usages.UsageView;
import java.util.List;
/**
* @author Manuel Stadelmann
*/
public class RemoveUsageAction extends IncludeExcludeActionBase {
@Override
protected void process(Usage[] usages, UsageView usageView) {
Usage nextToSelect = null;
for (Usage usage : usages) {
Usage toSelect = getNextToSelect(usageView, usage);
usageView.removeUsage(usage);
nextToSelect = toSelect;
}
if (nextToSelect != null) {
usageView.selectUsages(new Usage[]{nextToSelect});
}
}
private Usage getNextToSelect(UsageView usageView, Usage toDelete) {
List<Usage> sortedUsages = usageView.getSortedUsages();
int curIndex = sortedUsages.indexOf(toDelete);
int selectIndex = 0;
if (curIndex < sortedUsages.size() - 1) {
selectIndex = curIndex + 1;
}
else if (curIndex > 0) {
selectIndex = curIndex - 1;
}
return sortedUsages.get(selectIndex);
}
}
@@ -645,13 +645,35 @@ public class FileUtil extends FileUtilRt {
/**
* Converts given path to canonical representation by eliminating '.'s, traversing '..'s, and omitting duplicate separators.
* Please note that this method is symlink-unfriendly (i.e. result of "/path/to/link/../next" most probably will differ from
* what {@link java.io.File#getCanonicalPath()} will return) - so use with care.
* what {@link java.io.File#getCanonicalPath()} will return) - so use with care.<br>
* <br>
* If the path may contain synlinks, use use {@link FileUtil#toCanonicalPath(String, boolean)}
*/
@Contract("null -> null")
public static String toCanonicalPath(@Nullable String path) {
return toCanonicalPath(path, File.separatorChar, true);
}
/**
* When relative ../ parts do not escape outside of symlinks, the links are not expanded.<br>
* That is, in the best-case scenario the original non-expanded path is preserved.<br>
* <br>
* Otherwise, returns a fully resolved path using {@link java.io.File#getCanonicalPath()}.<br>
* <br>
* Consider the following case:
* <pre>
* root/
* dir1/
* link_to_dir1
* dir2/
* </pre>
* 'root/dir1/link_to_dir1/../dir2' should be resolved to 'root/dir2'
*/
@Contract("null, _ -> null")
public static String toCanonicalPath(@Nullable String path, boolean resolveSymlinksIfNecessary) {
return toCanonicalPath(path, File.separatorChar, true, resolveSymlinksIfNecessary);
}
@Contract("null, _ -> null")
public static String toCanonicalPath(@Nullable String path, char separatorChar) {
return toCanonicalPath(path, separatorChar, true);
@@ -664,6 +686,14 @@ public class FileUtil extends FileUtilRt {
@Contract("null, _, _ -> null")
private static String toCanonicalPath(@Nullable String path, char separatorChar, boolean removeLastSlash) {
return toCanonicalPath(path, separatorChar, removeLastSlash, false);
}
@Contract("null, _, _, _ -> null")
private static String toCanonicalPath(@Nullable String path,
final char separatorChar,
final boolean removeLastSlash,
final boolean resolveSymlinksIfNecessary) {
if (path == null || path.isEmpty()) {
return path;
}
@@ -676,6 +706,21 @@ public class FileUtil extends FileUtilRt {
return path;
}
final String finalPath = path;
NotNullProducer<String> realCanonicalPath = !resolveSymlinksIfNecessary ? null : new NotNullProducer<String>() {
@NotNull
@Override
public String produce() {
try {
return new File(finalPath).getCanonicalPath().replace(separatorChar, '/');
}
catch (IOException ignore) {
// fall back to the default behavior
return toCanonicalPath(finalPath, separatorChar, removeLastSlash, false);
}
}
};
StringBuilder result = new StringBuilder(path.length());
int start = processRoot(path, result);
int dots = 0;
@@ -685,7 +730,9 @@ public class FileUtil extends FileUtilRt {
char c = path.charAt(i);
if (c == '/') {
if (!separator) {
processDots(result, dots, start);
if (!processDots(result, dots, start, resolveSymlinksIfNecessary)) {
return realCanonicalPath.produce();
}
dots = 0;
}
separator = true;
@@ -710,7 +757,9 @@ public class FileUtil extends FileUtilRt {
}
if (dots > 0) {
processDots(result, dots, start);
if (!processDots(result, dots, start, resolveSymlinksIfNecessary)) {
return realCanonicalPath.produce();
}
}
int lastChar = result.length() - 1;
@@ -754,7 +803,8 @@ public class FileUtil extends FileUtilRt {
return 0;
}
private static void processDots(@NotNull StringBuilder result, int dots, int start) {
@Contract("_, _, _, false -> true")
private static boolean processDots(@NotNull StringBuilder result, int dots, int start, boolean resolveSymlinksIfNecessary) {
if (dots == 2) {
int pos = -1;
if (!StringUtil.endsWith(result, "/../") && !StringUtil.equals(result, "../")) {
@@ -770,6 +820,9 @@ public class FileUtil extends FileUtilRt {
}
}
if (pos >= 0) {
if (resolveSymlinksIfNecessary && FileSystemUtil.isSymLink(new File(result.toString()))) {
return false;
}
result.delete(pos, result.length());
}
else {
@@ -780,6 +833,7 @@ public class FileUtil extends FileUtilRt {
StringUtil.repeatSymbol(result, '.', dots);
result.append('/');
}
return true;
}
/**
@@ -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.
@@ -29,6 +29,13 @@ public class OrderedSet<T> extends ArrayList<T> implements Set<T>, RandomAccess
this(ContainerUtil.<T>canonicalStrategy());
}
public OrderedSet(@NotNull Collection<T> set) {
super(set.size());
myHashSet = new OpenTHashSet<T>(set.size());
addAll(set);
}
public OrderedSet(@NotNull TObjectHashingStrategy<T> hashingStrategy) {
this(hashingStrategy, 4);
}
@@ -38,6 +45,12 @@ public class OrderedSet<T> extends ArrayList<T> implements Set<T>, RandomAccess
myHashSet = new OpenTHashSet<T>(capacity, hashingStrategy);
}
public OrderedSet(int capacity) {
super(capacity);
myHashSet = new OpenTHashSet<T>(capacity);
}
@Override
public boolean removeAll(@NotNull Collection<?> c) {
boolean removed = false;

Some files were not shown because too many files have changed in this diff Show More