diff --git a/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpPropertyImpl.java b/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpPropertyImpl.java index 5c97ac35489b..d09833be2ced 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpPropertyImpl.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/psi/impl/RegExpPropertyImpl.java @@ -15,9 +15,9 @@ */ package org.intellij.lang.regexp.psi.impl; -import com.intellij.codeInsight.lookup.LookupValueFactory; -import com.intellij.codeInsight.lookup.LookupValueWithPriority; -import com.intellij.codeInsight.lookup.LookupValueWithUIHint; +import com.intellij.codeInsight.completion.PrioritizedLookupElement; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; @@ -25,6 +25,7 @@ import com.intellij.psi.PsiReference; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.PlatformIcons; +import com.intellij.util.containers.ContainerUtil; import org.intellij.lang.regexp.RegExpLanguageHosts; import org.intellij.lang.regexp.RegExpTT; import org.intellij.lang.regexp.psi.RegExpElementVisitor; @@ -32,10 +33,10 @@ import org.intellij.lang.regexp.psi.RegExpProperty; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.awt.*; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Collection; import java.util.List; public class RegExpPropertyImpl extends RegExpElementImpl implements RegExpProperty { @@ -100,67 +101,40 @@ public class RegExpPropertyImpl extends RegExpElementImpl implements RegExpPrope return false; } - @NotNull - public Object[] getVariants() { - final ASTNode categoryNode = getCategoryNode(); - if (categoryNode != null && categoryNode.getText().startsWith("In") && !categoryNode.getText().startsWith("Intelli")) { - return UNICODE_BLOCKS; - } else { - final String[][] knownProperties = RegExpLanguageHosts.getInstance().getAllKnownProperties(getElement()); - final Object[] objects = new Object[knownProperties.length]; - for (int i = 0; i < objects.length; i++) { - final String[] prop = knownProperties[i]; - objects[i] = new MyLookupValue(prop); - - } - return objects; - } - } - - public boolean isSoft() { - return true; - } - - private class MyLookupValue extends LookupValueFactory.LookupValueWithIcon implements LookupValueWithPriority, LookupValueWithUIHint { - private final String[] myProp; - - public MyLookupValue(String[] prop) { - super(prop[0], PlatformIcons.PROPERTY_ICON); - myProp = prop; - } - - public String getPresentation() { - final ASTNode categoryNode = getCategoryNode(); - if (categoryNode != null) { - if (categoryNode.getText().startsWith("Is")) { - return "Is" + super.getPresentation(); - } - } - return super.getPresentation(); - } - - public int getPriority() { - final String name = myProp[0]; - if (name.equals("all")) return HIGH + 1; - if (name.startsWith("java")) return HIGHER; - return name.length() > 2 ? HIGH : NORMAL; - } - - public String getTypeHint() { - return myProp.length > 1 ? myProp[1] : ("Character.is" + myProp[0].substring("java".length()) + "()"); - } - - @Nullable - public Color getColorHint() { - return null; - } - - public boolean isBold() { - return false; - } + @NotNull + public Object[] getVariants() { + final ASTNode categoryNode = getCategoryNode(); + if (categoryNode != null && categoryNode.getText().startsWith("In") && !categoryNode.getText().startsWith("Intelli")) { + return UNICODE_BLOCKS; + } + else { + boolean startsWithIs = categoryNode != null && categoryNode.getText().startsWith("Is"); + Collection result = ContainerUtil.newArrayList(); + for (String[] properties : RegExpLanguageHosts.getInstance().getAllKnownProperties(getElement())) { + String name = ArrayUtil.getFirstElement(properties); + if (name != null) { + String typeText = properties.length > 1 ? properties[1] : ("Character.is" + name.substring("java".length()) + "()"); + result.add(PrioritizedLookupElement.withPriority(LookupElementBuilder.create(name) + .withPresentableText(startsWithIs ? "Is" + name : name) + .withIcon(PlatformIcons.PROPERTY_ICON) + .withTypeText(typeText), getPriority(name))); + } } + return ArrayUtil.toObjectArray(result); + } } + private int getPriority(@NotNull String propertyName) { + if (propertyName.equals("all")) return 3; + if (propertyName.startsWith("java")) return 1; + if (propertyName.length() > 2) return 2; + return 0; + } + + public boolean isSoft() { + return true; + } + } private static final String[] UNICODE_BLOCKS; static { diff --git a/bin/scripts/unix/idea.sh b/bin/scripts/unix/idea.sh index f20ed48714d5..cfbb5880e9fd 100755 --- a/bin/scripts/unix/idea.sh +++ b/bin/scripts/unix/idea.sh @@ -107,13 +107,18 @@ else fi fi -if [ -z "$JDK" ]; then +JAVA_BIN="$JDK/bin/java" +if [ ! -x "$JAVA_BIN" ]; then + JAVA_BIN="$JDK/jre/bin/java" +fi + +if [ -z "$JDK" ] || [ ! -x "$JAVA_BIN" ]; then message "No JDK found. Please validate either @@product_uc@@_JDK, JDK_HOME or JAVA_HOME environment variable points to valid JDK installation." exit 1 fi VERSION_LOG=`"$MKTEMP" -t java.version.log.XXXXXX` -"$JDK/jre/bin/java" -version 2> "$VERSION_LOG" +"$JAVA_BIN" -version 2> "$VERSION_LOG" "$GREP" "64-Bit|x86_64|amd64" "$VERSION_LOG" > /dev/null BITS=$? "$RM" -f "$VERSION_LOG" @@ -175,7 +180,7 @@ fi # --------------------------------------------------------------------- # Run the IDE. # --------------------------------------------------------------------- -LD_LIBRARY_PATH="$IDE_BIN_HOME:$LD_LIBRARY_PATH" "$JDK/jre/bin/java" \ +LD_LIBRARY_PATH="$IDE_BIN_HOME:$LD_LIBRARY_PATH" "$JAVA_BIN" \ $AGENT \ "-Xbootclasspath/a:$IDE_HOME/lib/boot.jar" \ -classpath "$CLASSPATH" \ diff --git a/build/scripts/tests.gant b/build/scripts/tests.gant index fd2d537b9fbc..d23155dfd888 100644 --- a/build/scripts/tests.gant +++ b/build/scripts/tests.gant @@ -41,7 +41,6 @@ if (System.getProperty("os.name").toLowerCase().startsWith("mac")) { if (vendor != null && vendor.toLowerCase().contains("apple")) { args << "-d32" } - args << "-Dfile.encoding=UTF-8" }; args << "-Djna.nosys=true" diff --git a/build/scripts/utils.gant b/build/scripts/utils.gant index 0317193ce6c3..ab82ef6409fb 100644 --- a/build/scripts/utils.gant +++ b/build/scripts/utils.gant @@ -338,6 +338,7 @@ binding.setVariable("commonJvmArgsForTests", { return [ "-ea", + "-Dfile.encoding=UTF-8", "-Dio.netty.leakDetectionLevel=PARANOID", "-server", "-Xbootclasspath/p:${projectBuilder.moduleOutput(findModule("boot"))}", diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index 72391c13b2b4..a38b85ca186c 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -913,7 +913,7 @@ public class BuildManager implements Disposable { int sdkMinorVersion = 0; JavaSdkVersion sdkVersion = null; - final Set candidates = new HashSet(); + final Set candidates = new LinkedHashSet(); final Sdk defaultSdk = ProjectRootManager.getInstance(project).getProjectSdk(); if (defaultSdk != null && defaultSdk.getSdkType() instanceof JavaSdkType) { candidates.add(defaultSdk); diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java index e23f017b6de3..4cfc4ba97d7d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java @@ -126,6 +126,24 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler { } } + @Override + public void visitField(PsiField field) { + TextRange range = field.getTextRange(); + if (lineRange.intersects(range)) { + //textRange.set(textRange.get().union(range)); + super.visitField(field); + } + } + + @Override + public void visitMethod(PsiMethod method) { + TextRange range = method.getTextRange(); + if (lineRange.intersects(range)) { + //textRange.set(textRange.get().union(range)); + super.visitMethod(method); + } + } + @Override public void visitStatement(PsiStatement statement) { TextRange range = statement.getTextRange(); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java index 731b83bea4fb..2362976e3c57 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java @@ -46,6 +46,7 @@ import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; import com.intellij.xdebugger.evaluation.XInstanceEvaluator; import com.intellij.xdebugger.frame.*; +import com.intellij.xdebugger.frame.presentation.XErrorValuePresentation; import com.intellij.xdebugger.frame.presentation.XValuePresentation; import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.intellij.xdebugger.impl.evaluate.XValueCompactPresentation; @@ -134,7 +135,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV @Override protected void commandCancelled() { - node.setPresentation(null, new JavaValuePresentation("", null, DebuggerBundle.message("error.context.has.changed"), myValueDescriptor), false); + node.setPresentation(null, new XErrorValuePresentation(DebuggerBundle.message("error.context.has.changed")), false); } @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java index abc33f354eb9..ca27c8d0e4a0 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java @@ -484,6 +484,13 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { return lType instanceof PsiPrimitiveType && rType instanceof PsiClassType || lType instanceof PsiClassType && rType instanceof PsiPrimitiveType; } + // concat with a String + if (opCode == JavaTokenType.PLUS) { + if ((lType instanceof PsiClassType && lType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) || + (rType instanceof PsiClassType && rType.equalsToText(CommonClassNames.JAVA_LANG_STRING))){ + return false; + } + } // all other operations at least one should be of class type return lType instanceof PsiClassType || rType instanceof PsiClassType; } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java index 41242c5fdfa3..e8c1afd65b08 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java @@ -19,6 +19,7 @@ import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; import com.intellij.openapi.util.Couple; +import com.intellij.psi.CommonClassNames; import com.intellij.util.containers.HashMap; import com.sun.jdi.ClassType; import com.sun.jdi.Method; @@ -39,14 +40,14 @@ public class UnBoxingEvaluator implements Evaluator{ private final Evaluator myOperand; private static final Map> TYPES_TO_CONVERSION_METHOD_MAP = new HashMap>(); static { - TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Boolean", Couple.of("booleanValue", "()Z")); - TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Byte", Couple.of("byteValue", "()B")); - TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Character", Couple.of("charValue", "()C")); - TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Short", Couple.of("shortValue", "()S")); - TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Integer", Couple.of("intValue", "()I")); - TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Long", Couple.of("longValue", "()J")); - TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Float", Couple.of("floatValue", "()F")); - TYPES_TO_CONVERSION_METHOD_MAP.put("java.lang.Double", Couple.of("doubleValue", "()D")); + TYPES_TO_CONVERSION_METHOD_MAP.put(CommonClassNames.JAVA_LANG_BOOLEAN, Couple.of("booleanValue", "()Z")); + TYPES_TO_CONVERSION_METHOD_MAP.put(CommonClassNames.JAVA_LANG_BYTE, Couple.of("byteValue", "()B")); + TYPES_TO_CONVERSION_METHOD_MAP.put(CommonClassNames.JAVA_LANG_CHARACTER, Couple.of("charValue", "()C")); + TYPES_TO_CONVERSION_METHOD_MAP.put(CommonClassNames.JAVA_LANG_SHORT, Couple.of("shortValue", "()S")); + TYPES_TO_CONVERSION_METHOD_MAP.put(CommonClassNames.JAVA_LANG_INTEGER, Couple.of("intValue", "()I")); + TYPES_TO_CONVERSION_METHOD_MAP.put(CommonClassNames.JAVA_LANG_LONG, Couple.of("longValue", "()J")); + TYPES_TO_CONVERSION_METHOD_MAP.put(CommonClassNames.JAVA_LANG_FLOAT, Couple.of("floatValue", "()F")); + TYPES_TO_CONVERSION_METHOD_MAP.put(CommonClassNames.JAVA_LANG_DOUBLE, Couple.of("doubleValue", "()D")); } public static boolean isTypeUnboxable(String typeName) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java index f14ef8c48b23..af418f3053e8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java @@ -34,8 +34,8 @@ import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementFactory; -import com.intellij.psi.PsiExpression; import com.intellij.util.IncorrectOperationException; import com.intellij.xdebugger.settings.XDebuggerSettingsManager; import com.sun.jdi.*; @@ -229,7 +229,7 @@ public class ClassRenderer extends NodeRendererImpl{ } @Override - public PsiExpression getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) throws EvaluateException { + public PsiElement getChildValueExpression(DebuggerTreeNode node, DebuggerContext context) throws EvaluateException { FieldDescriptor fieldDescriptor = (FieldDescriptor)node.getDescriptor(); PsiElementFactory elementFactory = JavaPsiFacade.getInstance(node.getProject()).getElementFactory(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java index 7ada545eae81..71b13ea68419 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java @@ -95,7 +95,8 @@ public class HighlightControlFlowUtil { // do not compute constant expressions for if() statement condition // see JLS 14.20 Unreachable Statements try { - final ControlFlow controlFlow = getControlFlowNoConstantEvaluate(codeBlock); + AllVariablesControlFlowPolicy policy = AllVariablesControlFlowPolicy.getInstance(); + final ControlFlow controlFlow = ControlFlowFactory.getInstance(codeBlock.getProject()).getControlFlow(codeBlock, policy, false, false); final PsiElement unreachableStatement = ControlFlowUtil.getUnreachableStatement(controlFlow); if (unreachableStatement != null) { String description = JavaErrorMessages.message("unreachable.statement"); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index af88115801c4..0f44a9171846 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -182,7 +182,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh highlight.run(); progress.checkCanceled(); HighlightingSession highlightingSession = HighlightingSessionImpl.getHighlightingSession(file, progress); - PostHighlightingVisitor highlightingVisitor = new PostHighlightingVisitor(file, document, refCountHolder, highlightingSession); + PostHighlightingVisitor highlightingVisitor = new PostHighlightingVisitor(file, document, refCountHolder); highlightingVisitor.collectHighlights(file, holder, progress); } }); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java index 3f89ae26ff55..1d1644d7dfd5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java @@ -73,7 +73,6 @@ class PostHighlightingVisitor { @NotNull private final Project myProject; private final PsiFile myFile; @NotNull private final Document myDocument; - @NotNull private final HighlightingSession myHighlightingSession; private boolean myHasRedundantImports; private int myCurrentEntryIndex; @@ -94,19 +93,19 @@ class PostHighlightingVisitor { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - if (myProject.isDisposed() || !myFile.isValid()) return; + if (myProject.isDisposed() || !myFile.isValid() || !myFile.isWritable()) return; IntentionAction optimizeImportsFix = QuickFixFactory.getInstance().createOptimizeImportsFix(true); - if (optimizeImportsFix.isAvailable(myProject, null, myFile) && myFile.isWritable()) { + if (optimizeImportsFix.isAvailable(myProject, null, myFile)) { optimizeImportsFix.invoke(myProject, null, myFile); } } }); } }; - Disposer.register(myHighlightingSession, invokeFixLater); + Disposer.register((DaemonProgressIndicator)progress, invokeFixLater); if (progress.isCanceled()) { Disposer.dispose(invokeFixLater); - Disposer.dispose(myHighlightingSession); + Disposer.dispose((DaemonProgressIndicator)progress); progress.checkCanceled(); } } @@ -114,9 +113,7 @@ class PostHighlightingVisitor { PostHighlightingVisitor(@NotNull PsiFile file, @NotNull Document document, - @NotNull RefCountHolder refCountHolder, - @NotNull HighlightingSession highlightingSession) throws ProcessCanceledException { - myHighlightingSession = highlightingSession; + @NotNull RefCountHolder refCountHolder) throws ProcessCanceledException { myProject = file.getProject(); myFile = file; myDocument = document; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java index 953ac60f2bc1..ade25c7b1d5d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java @@ -211,7 +211,7 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return null; final PsiAnonymousClass anonymousClass = ((PsiNewExpression)element).getAnonymousClass(); - LOG.assertTrue(anonymousClass != null); + if (anonymousClass == null) return null; ChangeContextUtil.encodeContextInfo(anonymousClass, true); final PsiElement lambdaContext = anonymousClass.getParent().getParent(); @@ -229,10 +229,10 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection } else { method = anonymousClass.getMethods()[0]; } - LOG.assertTrue(method != null); + if (method == null) return null; final PsiCodeBlock body = method.getBody(); - LOG.assertTrue(body != null); + if (body == null) return null; final ForbiddenRefsChecker checker = new ForbiddenRefsChecker(method, anonymousClass); body.accept(checker); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/ex/EntryPointsManagerBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/ex/EntryPointsManagerBase.java index bad569517aae..9539d2d598ab 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/ex/EntryPointsManagerBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/ex/EntryPointsManagerBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,6 @@ import com.intellij.openapi.extensions.impl.ExtensionPointImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizableStringList; import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.psi.PsiDocCommentOwner; @@ -140,7 +139,7 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen try { ADDITIONAL_ANNOTATIONS.readExternal(element); } - catch (InvalidDataException ignored) { + catch (Throwable ignored) { } } diff --git a/java/java-analysis-impl/src/com/intellij/find/findUsages/JavaFindUsagesHelper.java b/java/java-analysis-impl/src/com/intellij/find/findUsages/JavaFindUsagesHelper.java index e836d2584c13..38563a0d62da 100644 --- a/java/java-analysis-impl/src/com/intellij/find/findUsages/JavaFindUsagesHelper.java +++ b/java/java-analysis-impl/src/com/intellij/find/findUsages/JavaFindUsagesHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ public class JavaFindUsagesHelper { private static final Logger LOG = Logger.getInstance("#com.intellij.find.findUsages.JavaFindUsagesHelper"); @NotNull - static Set getElementNames(@NotNull final PsiElement element) { + public static Set getElementNames(@NotNull final PsiElement element) { if (element instanceof PsiDirectory) { // normalize a directory to a corresponding package PsiPackage aPackage = ApplicationManager.getApplication().runReadAction(new Computable() { @Override diff --git a/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java b/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java index 19ea13fecc15..e9039aba119b 100644 --- a/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java +++ b/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java @@ -90,6 +90,10 @@ public class FindSuperElementsHelper { if (superInterface == null) { continue; } + if (containingClass.isInheritor(superInterface, true)) { + // if containingClass implements the superInterface then it's not a sibling inheritance but a pretty boring the usual one + continue; + } // calculate substitutor of containingClass --> inheritor PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(containingClass, inheritor, PsiSubstitutor.EMPTY); @@ -100,10 +104,11 @@ public class FindSuperElementsHelper { final MethodSignature derivedSignature = method.getSignature(PsiSubstitutor.EMPTY); boolean isOverridden = MethodSignatureUtil.isSubsignature(superSignature, derivedSignature); - if (isOverridden) { - result[0] = superMethod; - return false; + if (!isOverridden) { + continue; } + result[0] = superMethod; + return false; } } return true; diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java index 16e2e6512c34..0ef73a48b7b7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java @@ -16,7 +16,10 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.guess.GuessManager; -import com.intellij.codeInsight.lookup.*; +import com.intellij.codeInsight.lookup.ExpressionLookupItem; +import com.intellij.codeInsight.lookup.KeywordLookupItem; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.VariableLookupItem; import com.intellij.codeInsight.template.SmartCompletionContextType; import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.codeInsight.template.impl.TemplateSettings; @@ -28,7 +31,6 @@ import com.intellij.psi.scope.BaseScopeProcessor; import com.intellij.psi.scope.util.PsiScopesUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Consumer; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -44,13 +46,7 @@ public class BasicExpressionCompletionContributor { } public static LookupElement createKeywordLookupItem(final PsiElement element, final String s) { - try { - final PsiKeyword keyword = JavaPsiFacade.getInstance(element.getProject()).getElementFactory().createKeyword(s, element); - return new KeywordLookupItem(keyword, element).setAutoCompletionPolicy(AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE); - } - catch (IncorrectOperationException e) { - throw new RuntimeException(e); - } + return new KeywordLookupItem(JavaPsiFacade.getElementFactory(element.getProject()).createKeyword(s, element), element); } public static void fillCompletionVariants(JavaSmartCompletionParameters parameters, diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java b/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java index 64a16addf3c3..4c81a4e2a87a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java @@ -178,7 +178,7 @@ public class ConstructorInsertHandler implements InsertHandler tail && Character.isLetter(text.charAt(tail))) { + context.getDocument().insertString(tail, " "); + } } if (fillTypeArgs && context.getCompletionChar() != '(') { @@ -164,15 +171,12 @@ class JavaClassNameInsertHandler implements InsertHandler)_ret).setAttribute(LookupItem.SUBSTITUTOR, substitutor); + if (_ret instanceof LookupItem) { + final PsiSubstitutor substitutor = completionElement.getSubstitutor(); + if (substitutor != null) { + ((LookupItem)_ret).setAttribute(LookupItem.SUBSTITUTOR, substitutor); + } } - return Collections.singletonList(_ret); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java index 0919bb9b93de..9698aa389779 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java @@ -89,7 +89,7 @@ public class JavaDocCompletionContributor extends CompletionContributor { for (final CompletionElement _item : processor.getResults()) { final Object element = _item.getElement(); - LookupItem item = createLookupItem(element); + LookupElement item = createLookupItem(element); if (onlyConstants) { Object o = item.getObject(); if (!(o instanceof PsiField)) continue; @@ -100,16 +100,20 @@ public class JavaDocCompletionContributor extends CompletionContributor { item.putUserData(LookupItem.FORCE_SHOW_SIGNATURE_ATTR, Boolean.TRUE); if (isArg) { - item.setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); + item = AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(item); } result.addElement(item); } JavaCompletionContributor.addAllClasses(parameters, result, new InheritorsHolder(result)); } + + if (tag != null && "author".equals(tag.getName())) { + result.addElement(LookupElementBuilder.create(SystemProperties.getUserName())); + } } - private LookupItem createLookupItem(final Object element) { + private LookupElement createLookupItem(final Object element) { if (element instanceof PsiMethod) { return new JavaMethodCallElement((PsiMethod)element) { @Override @@ -124,7 +128,7 @@ public class JavaDocCompletionContributor extends CompletionContributor { return classElement; } - return (LookupItem)LookupItemUtil.objectToLookupItem(element); + return LookupItemUtil.objectToLookupItem(element); } }); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java index 92f3271c4e94..ae95ee158638 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java @@ -328,12 +328,12 @@ public class JavaKeywordCompletion { result.consume(createKeyword(position, PsiKeyword.THIS)); } - final LookupItem superItem = (LookupItem)createKeyword(position, PsiKeyword.SUPER); + final LookupElement superItem = createKeyword(position, PsiKeyword.SUPER); if (psiElement().afterLeaf(psiElement().withText("{").withSuperParent(2, psiMethod().constructor(true))).accepts(position)) { final PsiMethod method = PsiTreeUtil.getParentOfType(position, PsiMethod.class, false, PsiClass.class); assert method != null; final boolean hasParams = superConstructorHasParameters(method); - superItem.setInsertHandler(new ParenthesesInsertHandler() { + result.consume(LookupElementDecorator.withInsertHandler(superItem, new ParenthesesInsertHandler() { @Override protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) { return hasParams; @@ -344,7 +344,8 @@ public class JavaKeywordCompletion { super.handleInsert(context, item); TailType.insertChar(context.getEditor(), context.getTailOffset(), ';'); } - }); + })); + return; } result.consume(superItem); @@ -468,6 +469,9 @@ public class JavaKeywordCompletion { if (referenceList != null && referenceList.getParent() instanceof PsiClass) { psiClass = (PsiClass)referenceList.getParent(); } + else if (prevLeaf.getParent() instanceof PsiTypeParameterList && prevLeaf.getParent().getParent() instanceof PsiClass) { + psiClass = (PsiClass)prevLeaf.getParent().getParent(); + } } if (psiClass != null) { @@ -643,7 +647,7 @@ public class JavaKeywordCompletion { br = TailTypeDecorator.withTail(br, tailType); cont = TailTypeDecorator.withTail(cont, tailType); - if (loop != null && new InsideElementFilter(new ClassFilter(PsiStatement.class)).isAcceptable(position, loop)) { + if (loop != null && PsiTreeUtil.isAncestor(loop.getBody(), position, false)) { result.consume(br); result.consume(cont); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaPsiClassReferenceElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaPsiClassReferenceElement.java index 789117879bd2..6a4f29ec6020 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaPsiClassReferenceElement.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaPsiClassReferenceElement.java @@ -16,10 +16,7 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.TailType; -import com.intellij.codeInsight.lookup.DefaultLookupItemRenderer; -import com.intellij.codeInsight.lookup.LookupElementPresentation; -import com.intellij.codeInsight.lookup.LookupItem; -import com.intellij.codeInsight.lookup.PsiTypeLookupItem; +import com.intellij.codeInsight.lookup.*; import com.intellij.codeInsight.lookup.impl.JavaElementLookupRenderer; import com.intellij.openapi.util.ClassConditionKey; import com.intellij.openapi.util.Comparing; @@ -41,7 +38,7 @@ import java.util.Set; /** * @author peter */ -public class JavaPsiClassReferenceElement extends LookupItem { +public class JavaPsiClassReferenceElement extends LookupItem implements TypedLookupItem { public static final Key PACKAGE_NAME = Key.create("PACKAGE_NAME"); public static final ClassConditionKey CLASS_CONDITION_KEY = ClassConditionKey.create(JavaPsiClassReferenceElement.class); private final Object myClass; @@ -63,6 +60,14 @@ public class JavaPsiClassReferenceElement extends LookupItem { return myForcedPresentableName; } + @Nullable + @Override + public PsiType getType() { + PsiClass psiClass = getObject(); + final PsiSubstitutor substitutor = (PsiSubstitutor)getAttribute(LookupItem.SUBSTITUTOR); + return JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass, substitutor == null ? PsiSubstitutor.EMPTY : substitutor); + } + @NotNull @Override public String getLookupString() { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/NullSmartCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/NullSmartCompletionContributor.java index fdfd1b3f93f0..dfca2a9d7bfd 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/NullSmartCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/NullSmartCompletionContributor.java @@ -16,11 +16,10 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.ExpectedTypeInfo; -import com.intellij.codeInsight.lookup.LookupItem; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiKeyword; import com.intellij.psi.PsiPrimitiveType; -import com.intellij.psi.PsiType; import java.util.Collection; import java.util.LinkedHashSet; @@ -52,8 +51,7 @@ public class NullSmartCompletionContributor extends CompletionContributor{ for (final ExpectedTypeInfo info : infos) { if (!(info.getType() instanceof PsiPrimitiveType)) { - final LookupItem item = (LookupItem)BasicExpressionCompletionContributor.createKeywordLookupItem(parameters.getPosition(), PsiKeyword.NULL); - item.setAttribute(LookupItem.TYPE, PsiType.NULL); + final LookupElement item = BasicExpressionCompletionContributor.createKeywordLookupItem(parameters.getPosition(), PsiKeyword.NULL); result.addElement(JavaSmartCompletionContributor.decorate(item, infos)); return; } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java index da917f1116fc..515f2b16ec6f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java @@ -17,11 +17,8 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.lookup.ExpressionLookupItem; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupItem; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.text.StringUtil; @@ -34,7 +31,6 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.filters.*; import com.intellij.psi.filters.element.ModifierFilter; import com.intellij.psi.filters.types.AssignableFromFilter; -import com.intellij.psi.impl.source.PostprocessReformattingAspect; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; @@ -254,10 +250,7 @@ public class ReferenceExpressionCompletionContributor { final PsiExpression[] dimensions = newExpression.getArrayDimensions(); if (dimensions.length == 1 && "1".equals(dimensions[0].getText()) && newExpression.getArrayInitializer() == null) { final String text = variable.getName() + "[0]"; - final PsiExpression conversion = createExpression(text, element); - ExpressionLookupItem result = new ExpressionLookupItem(conversion); - result.setIcon(variable.getIcon(Iconable.ICON_FLAG_VISIBILITY)); - return result; + return new ExpressionLookupItem(createExpression(text, element), variable.getIcon(Iconable.ICON_FLAG_VISIBILITY), text, text); } } } @@ -311,29 +304,17 @@ public class ReferenceExpressionCompletionContributor { throws IncorrectOperationException { if (itemType instanceof PsiArrayType && expectedType.isAssignableFrom(((PsiArrayType)itemType).getComponentType())) { final PsiExpression conversion = createExpression(getQualifierText(qualifier) + prefix + "[0]", element); - final LookupItem item = new ExpressionLookupItem(conversion); - - @NonNls final String presentable = prefix + "[...]"; - item.setLookupString(prefix); - item.setPresentableText(presentable); - item.addLookupStrings(prefix); - item.setIcon(object.getIcon(Iconable.ICON_FLAG_VISIBILITY)); - item.setInsertHandler(new InsertHandler() { + result.consume(new ExpressionLookupItem(conversion, object.getIcon(Iconable.ICON_FLAG_VISIBILITY), prefix + "[...]", prefix) { @Override - public void handleInsert(InsertionContext context, LookupElement item) { + public void handleInsert(InsertionContext context) { FeatureUsageTracker.getInstance().triggerFeatureUsed(JavaCompletionFeatures.SECOND_SMART_COMPLETION_ARRAY_MEMBER); - final Editor editor = context.getEditor(); - final int startOffset = context.getStartOffset(); - final Document document = editor.getDocument(); - final int tailOffset = startOffset + item.getLookupString().length(); + final int tailOffset = context.getTailOffset(); final String callSpace = getSpace(CodeStyleSettingsManager.getSettings(element.getProject()).SPACE_WITHIN_BRACKETS); - final String access = "[" + callSpace + callSpace + "]"; - document.insertString(tailOffset, access); - editor.getCaretModel().moveToOffset(tailOffset + 1 + callSpace.length()); + context.getDocument().insertString(tailOffset, "[" + callSpace + callSpace + "]"); + context.getEditor().getCaretModel().moveToOffset(tailOffset + 1 + callSpace.length()); } }); - result.consume(item); } } @@ -358,41 +339,23 @@ public class ReferenceExpressionCompletionContributor { final String qualifierText = getQualifierText(qualifier); final PsiExpression conversion = createExpression("java.util.Arrays." + methodName + "(" + qualifierText + prefix + ")", element); - final LookupItem item = new ExpressionLookupItem(conversion); - - @NonNls final String presentable = "Arrays." + methodName + "(" + qualifierText + prefix + ")"; - item.setLookupString(StringUtil.isEmpty(qualifierText) ? presentable : prefix); - item.setPresentableText(presentable); - item.addLookupStrings(prefix, presentable, methodName + "(" + prefix + ")"); - item.setIcon(PlatformIcons.METHOD_ICON); - item.setInsertHandler(new InsertHandler() { + final String presentable = "Arrays." + methodName + "(" + qualifierText + prefix + ")"; + String[] lookupStrings = {StringUtil.isEmpty(qualifierText) ? presentable : prefix, prefix, presentable, methodName + "(" + prefix + ")"}; + result.consume(new ExpressionLookupItem(conversion, PlatformIcons.METHOD_ICON, presentable, lookupStrings) { @Override - public void handleInsert(InsertionContext context, LookupElement item) { + public void handleInsert(InsertionContext context) { FeatureUsageTracker.getInstance().triggerFeatureUsed(JavaCompletionFeatures.SECOND_SMART_COMPLETION_ASLIST); - final Editor editor = context.getEditor(); - int startOffset = context.getStartOffset(); - final Document document = editor.getDocument(); - final int tailOffset = startOffset + item.getLookupString().length(); - startOffset -= qualifierText.length(); + int startOffset = context.getStartOffset() - qualifierText.length(); final Project project = element.getProject(); final String callSpace = getSpace(CodeStyleSettingsManager.getSettings(project).SPACE_WITHIN_METHOD_CALL_PARENTHESES); - @NonNls final String newText = "java.util.Arrays." + methodName + "(" + callSpace + qualifierText + prefix + callSpace + ")"; - document.replaceString(startOffset, tailOffset, newText); + final String newText = "java.util.Arrays." + methodName + "(" + callSpace + qualifierText + prefix + callSpace + ")"; + context.getDocument().replaceString(startOffset, context.getTailOffset(), newText); - PsiDocumentManager.getInstance(project).commitDocument(document); - final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document); - try { - JavaCodeStyleManager.getInstance(project) - .shortenClassReferences(file, startOffset, startOffset + CommonClassNames.JAVA_UTIL_ARRAYS.length()); - } - catch (IncorrectOperationException e) { - LOG.error(e); - } - PostprocessReformattingAspect.getInstance(project).doPostponedFormatting(); + context.commitDocument(); + JavaCodeStyleManager.getInstance(project).shortenClassReferences(context.getFile(), startOffset, startOffset + CommonClassNames.JAVA_UTIL_ARRAYS.length()); } }); - result.consume(item); } @Nullable @@ -585,33 +548,16 @@ public class ReferenceExpressionCompletionContributor { return; } - final LookupItem item = new ExpressionLookupItem(conversion); - item.setLookupString(prefix + ".toArray(" + getSpace(callSpace) + expressionString + getSpace(callSpace) + ")"); - item.setPresentableText(prefix + ".toArray(" + presentableString + ")"); - item.addLookupStrings(presentableString); - item.setIcon(PlatformIcons.METHOD_ICON); - item.setInsertHandler(new InsertHandler(){ - @Override - public void handleInsert(InsertionContext context, LookupItem item) { - FeatureUsageTracker.getInstance().triggerFeatureUsed(JavaCompletionFeatures.SECOND_SMART_COMPLETION_TOAR); + String[] lookupStrings = {prefix + ".toArray(" + getSpace(callSpace) + expressionString + getSpace(callSpace) + ")", presentableString}; + result.consume(new ExpressionLookupItem(conversion, PlatformIcons.METHOD_ICON, prefix + ".toArray(" + presentableString + ")", lookupStrings) { + @Override + public void handleInsert(InsertionContext context) { + FeatureUsageTracker.getInstance().triggerFeatureUsed(JavaCompletionFeatures.SECOND_SMART_COMPLETION_TOAR); - final Editor editor = context.getEditor(); - final int startOffset = context.getStartOffset(); - final Document document = editor.getDocument(); - final int tailOffset = startOffset + item.getLookupString().length(); - final Project project = editor.getProject(); - context.commitDocument(); - final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document); - try { - JavaCodeStyleManager.getInstance(project).shortenClassReferences(file, startOffset, tailOffset); + context.commitDocument(); + JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(context.getFile(), context.getStartOffset(), context.getTailOffset()); } - catch (IncorrectOperationException e) { - LOG.error(e); - } - PostprocessReformattingAspect.getInstance(project).doPostponedFormatting(); - } - }); - result.consume(item); + }); } private static boolean isEmptyArrayInitializer(@Nullable PsiElement element) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java index 7f6920917c96..8c7af834937d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java @@ -217,7 +217,7 @@ class TypeArgumentCompletionProvider extends CompletionProvider handler; private final Function myTooltip; + @NotNull private final String myDebugName; + /** + * @deprecated use {@link #MarkerType(String, Function, LineMarkerNavigator)} instead + */ public MarkerType(@NotNull Function tooltip, @NotNull final LineMarkerNavigator navigator) { + this("Unknown", tooltip, navigator); + } + + public MarkerType(@NotNull String debugName, @NotNull Function tooltip, @NotNull final LineMarkerNavigator navigator) { myTooltip = tooltip; + myDebugName = debugName; handler = new GutterIconNavigationHandler() { @Override public void navigate(final MouseEvent e, final PsiElement elt) { @@ -77,6 +86,11 @@ public class MarkerType { }; } + @Override + public String toString() { + return myDebugName; + } + @NotNull public GutterIconNavigationHandler getNavigationHandler() { return handler; @@ -87,7 +101,7 @@ public class MarkerType { return myTooltip; } - static final MarkerType OVERRIDING_METHOD = new MarkerType(new NullableFunction() { + static final MarkerType OVERRIDING_METHOD = new MarkerType("OVERRIDING_METHOD", new NullableFunction() { @Override public String fun(PsiElement element) { PsiElement parent = getParentMethod(element); @@ -169,7 +183,7 @@ public class MarkerType { } public static final String SEARCHING_FOR_OVERRIDING_METHODS = "Searching for Overriding Methods"; - static final MarkerType OVERRIDDEN_METHOD = new MarkerType(new NullableFunction() { + static final MarkerType OVERRIDDEN_METHOD = new MarkerType("OVERRIDDEN_METHOD", new NullableFunction() { @Override public String fun(PsiElement element) { PsiElement parent = element.getParent(); @@ -257,7 +271,7 @@ public class MarkerType { } private static final String SEARCHING_FOR_OVERRIDDEN_METHODS = "Searching for Overridden Methods"; - static final MarkerType SUBCLASSED_CLASS = new MarkerType(new NullableFunction() { + static final MarkerType SUBCLASSED_CLASS = new MarkerType("SUBCLASSED_CLASS", new NullableFunction() { @Override public String fun(PsiElement element) { PsiElement parent = element.getParent(); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertSwitchToIfIntention.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertSwitchToIfIntention.java index 72df2025348f..44cd9f7dc587 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertSwitchToIfIntention.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertSwitchToIfIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiUtil; import com.intellij.util.IncorrectOperationException; +import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -117,7 +118,9 @@ public class ConvertSwitchToIfIntention implements IntentionAction { else { hadSideEffects = false; declarationString = null; - expressionText = switchExpression.getText(); + expressionText = ParenthesesUtils.getPrecedence(switchExpression) > ParenthesesUtils.EQUALITY_PRECEDENCE + ? '(' + switchExpression.getText() + ')' + : switchExpression.getText(); } final PsiCodeBlock body = switchStatement.getBody(); if (body == null) { diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/MethodCallFixer.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/MethodCallFixer.java index 240678e0726b..8a01a5a26334 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/MethodCallFixer.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/MethodCallFixer.java @@ -44,7 +44,7 @@ public class MethodCallFixer implements Fixer { if (args != null && !hasRParenth(args)) { int caret = editor.getCaretModel().getOffset(); - PsiCallExpression innermostCall = PsiTreeUtil.findElementOfClassAtOffset(psiElement.getContainingFile(), caret, PsiCallExpression.class, false); + PsiCallExpression innermostCall = PsiTreeUtil.findElementOfClassAtOffset(psiElement.getContainingFile(), caret - 1, PsiCallExpression.class, false); if (innermostCall == null) return; args = innermostCall.getArgumentList(); diff --git a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java index 2637e6e1e91e..95a1258f6d22 100644 --- a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java +++ b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java @@ -29,7 +29,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.JavaSdk; import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.roots.PackageIndex; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.JDOMUtil; @@ -42,6 +41,7 @@ import com.intellij.psi.*; import com.intellij.psi.impl.JavaConstantExpressionEvaluator; import com.intellij.psi.impl.source.tree.JavaDocElementType; import com.intellij.psi.javadoc.*; +import com.intellij.psi.search.EverythingGlobalScope; import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiFormatUtilBase; import com.intellij.psi.util.PsiTreeUtil; @@ -385,7 +385,7 @@ public class JavaDocInfoGenerator { return null; if (docURLs != null) { - if (elementHasSourceCode()) { + if (buffer.length() > 0 && elementHasSourceCode()) { LOG.debug("Documentation for " + myElement + " was generated from source code, it wasn't found at following URLs: ", docURLs); } else { @@ -408,25 +408,24 @@ public class JavaDocInfoGenerator { } private boolean elementHasSourceCode() { - VirtualFile[] files; + PsiFileSystemItem[] items; if (myElement instanceof PsiDirectory) { final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)myElement); if (aPackage == null) return false; - files = PackageIndex.getInstance(myProject).getDirectoriesByPackageName(aPackage.getQualifiedName(), true); + items = aPackage.getDirectories(new EverythingGlobalScope(myProject)); } else if (myElement instanceof PsiPackage) { - files = PackageIndex.getInstance(myProject).getDirectoriesByPackageName(((PsiPackage)myElement).getQualifiedName(), true); + items = ((PsiPackage)myElement).getDirectories(new EverythingGlobalScope(myProject)); } else { PsiFile containingFile = myElement.getNavigationElement().getContainingFile(); if (containingFile == null) return false; - VirtualFile virtualFile = containingFile.getVirtualFile(); - if (virtualFile == null) return false; - files = new VirtualFile[] {virtualFile}; + items = new PsiFileSystemItem[] {containingFile}; } ProjectFileIndex projectFileIndex = ProjectFileIndex.SERVICE.getInstance(myProject); - for (VirtualFile file : files) { - if (projectFileIndex.isInSource(file)) return true; + for (PsiFileSystemItem item : items) { + VirtualFile file = item.getVirtualFile(); + if (file != null && projectFileIndex.isInSource(file)) return true; } return false; } @@ -683,10 +682,7 @@ public class JavaDocInfoGenerator { } private void generatePackageJavaDoc(final StringBuilder buffer, final PsiPackage psiPackage, boolean generatePrologueAndEpilogue) { - VirtualFile[] dirs = PackageIndex.getInstance(myProject).getDirectoriesByPackageName(psiPackage.getQualifiedName(), true); - for (VirtualFile dir : dirs) { - PsiDirectory directory = PsiManager.getInstance(myProject).findDirectory(dir); - if (directory == null) continue; + for (PsiDirectory directory : psiPackage.getDirectories(new EverythingGlobalScope(myProject))) { final PsiFile packageInfoFile = directory.findFile(PsiPackage.PACKAGE_INFO_FILE); if (packageInfoFile != null) { final ASTNode node = packageInfoFile.getNode(); diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/ExpressionLookupItem.java b/java/java-impl/src/com/intellij/codeInsight/lookup/ExpressionLookupItem.java index 2482e9fb74d5..16ba1a83aba5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/ExpressionLookupItem.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/ExpressionLookupItem.java @@ -17,38 +17,87 @@ package com.intellij.codeInsight.lookup; import com.intellij.psi.*; import com.intellij.util.PlatformIcons; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.Collections; +import java.util.Set; /** * @author peter */ -public class ExpressionLookupItem extends LookupItem implements TypedLookupItem { - public ExpressionLookupItem(final PsiExpression expression) { - super(expression, expression.getText()); +public class ExpressionLookupItem extends LookupElement implements TypedLookupItem { + private final PsiExpression myExpression; + private final Icon myIcon; + private final String myPresentableText; + private final String myLookupString; + private final Set myAllLookupStrings; + public ExpressionLookupItem(final PsiExpression expression) { + this(expression, getExpressionIcon(expression), expression.getText(), expression.getText()); + } + + public ExpressionLookupItem(final PsiExpression expression, @Nullable Icon icon, String presentableText, String... lookupStrings) { + myExpression = expression; + myPresentableText = presentableText; + myIcon = icon; + myLookupString = lookupStrings[0]; + myAllLookupStrings = Collections.unmodifiableSet(ContainerUtil.newHashSet(lookupStrings)); + } + + @Nullable + private static Icon getExpressionIcon(@NotNull PsiExpression expression) { if (expression instanceof PsiReferenceExpression) { - final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression; - final PsiElement element = referenceExpression.resolve(); + final PsiElement element = ((PsiReferenceExpression)expression).resolve(); if (element != null) { - setIcon(element.getIcon(0)); + return element.getIcon(0); } } if (expression instanceof PsiMethodCallExpression) { - setIcon(PlatformIcons.METHOD_ICON); + return PlatformIcons.METHOD_ICON; } + return null; + } + + @NotNull + @Override + public PsiExpression getObject() { + return myExpression; + } + + @Override + public void renderElement(LookupElementPresentation presentation) { + presentation.setIcon(myIcon); + presentation.setItemText(myPresentableText); + PsiType type = getType(); + presentation.setTypeText(type == null ? null : type.getPresentableText()); } @Override public PsiType getType() { - return getObject().getType(); + return myExpression.getType(); } @Override public boolean equals(final Object o) { - return o instanceof ExpressionLookupItem && getLookupString().equals(((ExpressionLookupItem)o).getLookupString()); + return o instanceof ExpressionLookupItem && myLookupString.equals(((ExpressionLookupItem)o).myLookupString); } @Override public int hashCode() { - return getLookupString().hashCode(); + return myLookupString.hashCode(); + } + + @NotNull + @Override + public String getLookupString() { + return myLookupString; + } + + @Override + public Set getAllLookupStrings() { + return myAllLookupStrings; } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/KeywordLookupItem.java b/java/java-impl/src/com/intellij/codeInsight/lookup/KeywordLookupItem.java index 9fc6cc4392fc..768167291827 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/KeywordLookupItem.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/KeywordLookupItem.java @@ -24,14 +24,32 @@ import org.jetbrains.annotations.NotNull; /** * @author peter */ -public class KeywordLookupItem extends LookupItem implements TypedLookupItem { +public class KeywordLookupItem extends LookupElement implements TypedLookupItem { private final PsiElement myPosition; + private final PsiKeyword myKeyword; public KeywordLookupItem(final PsiKeyword keyword, @NotNull PsiElement position) { - super(keyword, keyword.getText()); + myKeyword = keyword; myPosition = position; } + @NotNull + @Override + public Object getObject() { + return myKeyword; + } + + @NotNull + @Override + public String getLookupString() { + return myKeyword.getText(); + } + + @Override + public AutoCompletionPolicy getAutoCompletionPolicy() { + return AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE; + } + @Override public boolean equals(final Object o) { return o instanceof KeywordLookupItem && getLookupString().equals(((KeywordLookupItem)o).getLookupString()); diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/LookupItemUtil.java b/java/java-impl/src/com/intellij/codeInsight/lookup/LookupItemUtil.java index 269e5652f981..aaa1bb896a3c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/LookupItemUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/LookupItemUtil.java @@ -71,6 +71,7 @@ public class LookupItemUtil{ * @deprecated * @see LookupElementBuilder */ + @NotNull public static LookupElement objectToLookupItem(Object object) { if (object instanceof LookupElement) return (LookupElement)object; if (object instanceof PsiClass) { @@ -82,9 +83,6 @@ public class LookupItemUtil{ if (object instanceof PsiVariable) { return new VariableLookupItem((PsiVariable)object); } - if (object instanceof PsiKeyword) { - return new KeywordLookupItem((PsiKeyword)object, (PsiKeyword)object); - } if (object instanceof PsiExpression) { return new ExpressionLookupItem((PsiExpression) object); } diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/PackageLookupItem.java b/java/java-impl/src/com/intellij/codeInsight/lookup/PackageLookupItem.java index 0ccd769911c3..10b23c247bb1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/PackageLookupItem.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/PackageLookupItem.java @@ -19,21 +19,51 @@ import com.intellij.codeInsight.AutoPopupController; import com.intellij.codeInsight.TailType; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiJavaCodeReferenceCodeFragment; import com.intellij.psi.PsiPackage; +import com.intellij.util.PlatformIcons; +import org.jetbrains.annotations.NotNull; /** * @author peter */ -class PackageLookupItem extends LookupItem { +class PackageLookupItem extends LookupElement { + private final PsiPackage myPackage; + private final String myString; + public PackageLookupItem(PsiPackage pkg) { - super(pkg, StringUtil.notNullize(pkg.getName())); - setTailType(TailType.DOT); + myPackage = pkg; + myString = StringUtil.notNullize(myPackage.getName()); + } + + @NotNull + @Override + public Object getObject() { + return myPackage; + } + + @NotNull + @Override + public String getLookupString() { + return myString; + } + + @Override + public void renderElement(LookupElementPresentation presentation) { + super.renderElement(presentation); + presentation.setIcon(PlatformIcons.PACKAGE_ICON); } @Override public void handleInsert(InsertionContext context) { - super.handleInsert(context); - if (getTailType() == TailType.DOT || context.getCompletionChar() == '.') { + PsiFile file = context.getFile(); + boolean addDot = !(file instanceof PsiJavaCodeReferenceCodeFragment) || ((PsiJavaCodeReferenceCodeFragment)file).isClassesAccepted(); + if (addDot) { + context.setAddCompletionChar(false); + TailType.DOT.processTail(context.getEditor(), context.getTailOffset()); + } + if (addDot || context.getCompletionChar() == '.') { AutoPopupController.getInstance(context.getProject()).scheduleAutoPopup(context.getEditor()); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java b/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java index 2158c01d7f6a..7e1844ca1c6f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java @@ -39,7 +39,7 @@ import java.util.Set; /** * @author peter */ -public class PsiTypeLookupItem extends LookupItem { +public class PsiTypeLookupItem extends LookupItem implements TypedLookupItem { private static final InsertHandler DEFAULT_IMPORT_FIXER = new InsertHandler() { @Override public void handleInsert(InsertionContext context, PsiTypeLookupItem item) { @@ -65,13 +65,14 @@ public class PsiTypeLookupItem extends LookupItem { } @NotNull - public PsiType getPsiType() { + @Override + public PsiType getType() { Object object = getObject(); PsiType type = object instanceof PsiType ? (PsiType)object : JavaPsiFacade.getElementFactory(((PsiClass) object).getProject()).createType((PsiClass)object); for (int i = 0; i < getBracketsCount(); i++) { type = new PsiArrayType(type); } - return type; + return getSubstitutor().substitute(type); } @@ -191,18 +192,14 @@ public class PsiTypeLookupItem extends LookupItem { } - public static PsiTypeLookupItem createLookupItem(@NotNull PsiType type, @Nullable PsiElement context, boolean isDiamond, InsertHandler importFixer) { - final PsiType original = type; + public static PsiTypeLookupItem createLookupItem(@NotNull PsiType type, @Nullable PsiElement context, boolean isDiamond, InsertHandler importFixer) { int dim = 0; while (type instanceof PsiArrayType) { type = ((PsiArrayType)type).getComponentType(); dim++; } - PsiTypeLookupItem item = doCreateItem(type, context, dim, isDiamond, importFixer); - - item.setAttribute(TYPE, original); - return item; + return doCreateItem(type, context, dim, isDiamond, importFixer); } private static PsiTypeLookupItem doCreateItem(final PsiType type, diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/impl/JavaElementLookupRenderer.java b/java/java-impl/src/com/intellij/codeInsight/lookup/impl/JavaElementLookupRenderer.java index 3d7e0ffcefae..fe47735e8cd8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/impl/JavaElementLookupRenderer.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/impl/JavaElementLookupRenderer.java @@ -19,7 +19,6 @@ import com.intellij.codeInsight.completion.JavaCompletionUtil; import com.intellij.codeInsight.lookup.DefaultLookupItemRenderer; import com.intellij.codeInsight.lookup.LookupElementPresentation; import com.intellij.codeInsight.lookup.LookupItem; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.beanProperties.BeanPropertyElement; import com.intellij.psi.util.PsiUtilCore; @@ -33,63 +32,19 @@ import java.util.List; public class JavaElementLookupRenderer implements ElementLookupRenderer { @Override public boolean handlesItem(final Object element) { - return element instanceof PsiKeyword || element instanceof PsiExpression || - element instanceof PsiTypeElement || element instanceof BeanPropertyElement; + return element instanceof BeanPropertyElement; } @Override public void renderElement(final LookupItem item, final Object element, final LookupElementPresentation presentation) { presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(item, presentation.isReal())); - presentation.setItemText(getName(element, item)); + presentation.setItemText(PsiUtilCore.getName((PsiElement)element)); presentation.setStrikeout(isToStrikeout(item)); presentation.setTailText((String)item.getAttribute(LookupItem.TAIL_TEXT_ATTR), item.getAttribute(LookupItem.TAIL_TEXT_SMALL_ATTR) != null); - presentation.setTypeText(getTypeText(element, item)); - } - - private static String getName(final Object o, final LookupItem item) { - final String presentableText = item.getPresentableText(); - if (presentableText != null) { - return presentableText; - } - - String name = ""; - if (o instanceof PsiElement) { - final PsiElement element = (PsiElement)o; - if (element.isValid()) { - if (element instanceof PsiKeyword || element instanceof PsiExpression || element instanceof PsiTypeElement) { - name = element.getText(); - } else { - name = PsiUtilCore.getName(element); - } - } - } - - return StringUtil.notNullize(name); - } - - @Nullable - private static String getTypeText(final Object o, final LookupItem item) { - String text = null; - if (o instanceof PsiElement) { - final PsiElement element = (PsiElement)o; - if (element.isValid()) { - if (element instanceof PsiExpression){ - PsiExpression expression = (PsiExpression)element; - PsiType type = expression.getType(); - if (type != null){ - text = type.getPresentableText(); - } - } - else if (element instanceof BeanPropertyElement) { - return getTypeText(item, ((BeanPropertyElement)element).getPropertyType()); - } - } - } - - return text; + presentation.setTypeText(getTypeText(item, ((BeanPropertyElement)element).getPropertyType())); } @Nullable diff --git a/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java b/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java index 1663d31da4a8..82142fc63df5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java @@ -15,12 +15,14 @@ */ package com.intellij.codeInsight.navigation; -import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator; +import com.intellij.codeInsight.generation.actions.PresentableCodeInsightActionHandler; import com.intellij.codeInsight.navigation.actions.GotoSuperAction; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.ide.util.MethodCellRenderer; +import com.intellij.idea.ActionsBundle; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; @@ -33,7 +35,7 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; -public class JavaGotoSuperHandler implements CodeInsightActionHandler { +public class JavaGotoSuperHandler implements PresentableCodeInsightActionHandler { @Override public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) { FeatureUsageTracker.getInstance().triggerFeatureUsed(GotoSuperAction.FEATURE_ID); @@ -93,4 +95,19 @@ public class JavaGotoSuperHandler implements CodeInsightActionHandler { public boolean startInWriteAction() { return false; } + + @Override + public void update(@NotNull Editor editor, @NotNull PsiFile file, Presentation presentation) { + final PsiElement element = getElement(file, editor.getCaretModel().getOffset()); + final PsiElement containingElement = PsiTreeUtil.getParentOfType(element, PsiFunctionalExpression.class, PsiMember.class); + if (containingElement instanceof PsiClass) { + presentation.setText(ActionsBundle.actionText("GotoSuperClass")); + presentation.setDescription(ActionsBundle.actionText("GotoSuperClass")); + } + else { + presentation.setText(ActionsBundle.actionText("GotoSuperMethod")); + presentation.setDescription(ActionsBundle.actionText("GotoSuperMethod")); + } + + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/macro/ComponentTypeOfMacro.java b/java/java-impl/src/com/intellij/codeInsight/template/macro/ComponentTypeOfMacro.java index d4ecc14c003e..5ab66a10ad6b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/macro/ComponentTypeOfMacro.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/macro/ComponentTypeOfMacro.java @@ -20,7 +20,6 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.PsiTypeLookupItem; import com.intellij.codeInsight.template.*; import com.intellij.psi.PsiArrayType; -import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiType; import com.intellij.util.containers.ContainerUtil; @@ -49,7 +48,7 @@ public class ComponentTypeOfMacro extends Macro { for (LookupElement element : lookupItems) { PsiTypeLookupItem lookupItem = element.as(PsiTypeLookupItem.CLASS_CONDITION_KEY); if (lookupItem != null) { - PsiType psiType = lookupItem.getPsiType(); + PsiType psiType = lookupItem.getType(); if (psiType instanceof PsiArrayType) { result.add(PsiTypeLookupItem.createLookupItem(((PsiArrayType)psiType).getComponentType(), null)); } @@ -83,7 +82,7 @@ public class ComponentTypeOfMacro extends Macro { for (LookupElement element : elements) { PsiTypeLookupItem typeLookupItem = element.as(PsiTypeLookupItem.CLASS_CONDITION_KEY); if (typeLookupItem != null) { - PsiType psiType = typeLookupItem.getPsiType(); + PsiType psiType = typeLookupItem.getType(); if (psiType instanceof PsiArrayType) { return new PsiTypeResult(((PsiArrayType)psiType).getComponentType(), context.getProject()); } diff --git a/java/java-impl/src/com/intellij/codeInspection/MoveToPackageFix.java b/java/java-impl/src/com/intellij/codeInspection/MoveToPackageFix.java index 3fd32c34212b..a93f50361cf6 100644 --- a/java/java-impl/src/com/intellij/codeInspection/MoveToPackageFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/MoveToPackageFix.java @@ -78,12 +78,21 @@ public class MoveToPackageFix implements LocalQuickFix { private void chooseDirectoryAndMove(Project project, PsiFile myFile) { try { - PsiDirectory directory = MoveClassesOrPackagesUtil.chooseDestinationPackage(project, myTargetPackage, myFile.getContainingDirectory()); + String error; + PsiDirectory directory = null; + try { + directory = MoveClassesOrPackagesUtil.chooseDestinationPackage(project, myTargetPackage, myFile.getContainingDirectory()); - if (directory == null) { - return; + if (directory == null) { + return; + } + + error = RefactoringMessageUtil.checkCanCreateFile(directory, myFile.getName()); } - String error = RefactoringMessageUtil.checkCanCreateFile(directory, myFile.getName()); + catch (IncorrectOperationException e) { + error = e.getLocalizedMessage(); + } + if (error != null) { Messages.showMessageDialog(project, error, CommonBundle.getErrorTitle(), Messages.getErrorIcon()); return; diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFluentIterableInspection.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFluentIterableInspection.java index bfc25cb7e8f6..1b1337674244 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFluentIterableInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFluentIterableInspection.java @@ -56,8 +56,7 @@ public class GuavaFluentIterableInspection extends BaseJavaBatchLocalInspectionT return null; } final Project project = manager.getProject(); - final PsiClass fluentIterable = JavaPsiFacade.getInstance(project) - .findClass(GUAVA_FLUENT_ITERABLE, GlobalSearchScope.allScope(project)); + final PsiClass fluentIterable = JavaPsiFacade.getInstance(project).findClass(GUAVA_FLUENT_ITERABLE, file.getResolveScope()); if (fluentIterable == null) { return null; } @@ -394,7 +393,7 @@ public class GuavaFluentIterableInspection extends BaseJavaBatchLocalInspectionT if (parameterType instanceof PsiClassType) { final PsiClass resolvedParameterClass = ((PsiClassType)parameterType).resolve(); final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(methodCallExpression.getProject()); - final GlobalSearchScope scope = GlobalSearchScope.allScope(methodCallExpression.getProject()); + final GlobalSearchScope scope = methodCallExpression.getResolveScope(); final PsiClass optional = javaPsiFacade.findClass(GUAVA_OPTIONAL, scope); final PsiClass immutableMap = javaPsiFacade.findClass(GUAVA_IMMUTABLE_MAP, scope); if (resolvedParameterClass != null && @@ -474,16 +473,20 @@ public class GuavaFluentIterableInspection extends BaseJavaBatchLocalInspectionT final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project); final PsiElementFactory elementFactory = javaPsiFacade.getElementFactory(); final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); + final GuavaFluentIterableMethodConverters converters = new GuavaFluentIterableMethodConverters(elementFactory); for (SmartPsiElementPointer usage : myFoundUsages) { final PsiExpression element = usage.getElement(); if (element != null) { - GuavaFluentIterableMethodConverters.convert(element, elementFactory, codeStyleManager); + PsiElement converted = converters.convert(element); + if (converted != null) { + codeStyleManager.shortenClassReferences(converted); + } } } if (myVariable != null) { final PsiLocalVariable element = myVariable.getElement(); if (element != null) { - GuavaFluentIterableMethodConverters.convert(element, elementFactory, codeStyleManager); + codeStyleManager.shortenClassReferences(converters.convert(element)); } } } diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFluentIterableMethodConverters.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFluentIterableMethodConverters.java index 1a44ef29a563..ade6ce30c197 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFluentIterableMethodConverters.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFluentIterableMethodConverters.java @@ -18,9 +18,7 @@ package com.intellij.codeInspection.java18StreamApi; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; import com.intellij.psi.*; -import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; @@ -42,6 +40,11 @@ public class GuavaFluentIterableMethodConverters { private static final Map METHOD_INDEX = new HashMap(); private static final Map TO_OTHER_COLLECTION_METHODS = new HashMap(); private static final Set STOP_METHODS = new HashSet(); + private final PsiElementFactory myElementFactory; + + public GuavaFluentIterableMethodConverters(PsiElementFactory elementFactory) { + myElementFactory = elementFactory; + } static { METHOD_INDEX.put("allMatch", new FluentIterableMethodTransformer.OneParameterMethodTransformer(StreamApiConstants.ALL_MATCH + "(%s)", true)); @@ -86,52 +89,44 @@ public class GuavaFluentIterableMethodConverters { return STOP_METHODS.contains(methodName); } - public static void convert(final PsiLocalVariable localVariable, - final PsiElementFactory elementFactory, - final JavaCodeStyleManager codeStyleManager) { + public PsiLocalVariable convert(final PsiLocalVariable localVariable) { final PsiTypeElement typeElement = localVariable.getTypeElement(); final PsiReferenceParameterList generics = PsiTreeUtil.findChildOfType(typeElement, PsiReferenceParameterList.class); - typeElement.replace(elementFactory.createTypeElementFromText( + typeElement.replace(myElementFactory.createTypeElementFromText( StreamApiConstants.JAVA_UTIL_STREAM_STREAM + (generics == null ? "" : generics.getText()), null)); final PsiExpression initializer = localVariable.getInitializer(); if (initializer != null) { - PsiMethodCallExpression initializerMethodCall = (PsiMethodCallExpression)initializer; - convertMethodCallDeep(elementFactory, initializerMethodCall); + convertMethodCallDeep((PsiMethodCallExpression)initializer); } - codeStyleManager.shortenClassReferences(localVariable); + return localVariable; } - public static void convert(PsiExpression expression, - final PsiElementFactory elementFactory, - final JavaCodeStyleManager codeStyleManager) { + public PsiElement convert(PsiExpression expression) { if (expression instanceof PsiReferenceExpression) { final PsiElement expressionParent = expression.getParent(); if (expressionParent instanceof PsiReturnStatement || isIterableMethodParameter(expressionParent, expression)) { - expression = (PsiExpression)expression.replace( - elementFactory.createExpressionFromText(expression.getText() + ".collect(java.util.stream.Collectors.toList())", null)); - codeStyleManager.shortenClassReferences(expression); + return addCollectionToList(expression); } - return; + return null; } final PsiMethodCallExpression parentMethodCall = PsiTreeUtil.getParentOfType(expression, PsiMethodCallExpression.class); if (parentMethodCall != null && parentMethodCall.getMethodExpression().getQualifierExpression() == expression) { final PsiMethod seqTailMethod = parentMethodCall.resolveMethod(); if (seqTailMethod == null) { - return; + return null; } final PsiClass seqTailMethodClass = seqTailMethod.getContainingClass(); if (seqTailMethodClass != null && GuavaFluentIterableInspection.GUAVA_OPTIONAL.equals(seqTailMethodClass.getQualifiedName())) { - final PsiMethodCallExpression newParentMethodCall = - GuavaOptionalConverter.convertGuavaOptionalToJava(parentMethodCall, elementFactory); + final PsiMethodCallExpression newParentMethodCall = GuavaOptionalConverter.convertGuavaOptionalToJava(parentMethodCall, myElementFactory); expression = newParentMethodCall.getMethodExpression().getQualifierExpression(); } } if (expression instanceof PsiMethodCallExpression) { - expression = convertMethodCallDeep(elementFactory, (PsiMethodCallExpression)expression); + expression = convertMethodCallDeep((PsiMethodCallExpression)expression); } if (expression == null) { - return; + return null; } final PsiElement parent = expression.getParent(); if (parent instanceof PsiExpressionList) { @@ -142,28 +137,28 @@ public class GuavaFluentIterableMethodConverters { final PsiMethod method = methodCall.resolveMethod(); LOG.assertTrue(method != null); final PsiType parameterType = method.getParameterList().getParameters()[index].getType(); - expression = addCollectToListIfNeed(expression, parameterType, elementFactory); - } else if (parent instanceof PsiReturnStatement) { + return addCollectToListIfNeed(expression, parameterType); + } + else if (parent instanceof PsiReturnStatement) { final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(parent, PsiMethod.class); LOG.assertTrue(containingMethod != null); final PsiType returnType = containingMethod.getReturnType(); - expression = addCollectToListIfNeed(expression, returnType, elementFactory); - } - codeStyleManager.shortenClassReferences(expression); - } - - private static PsiExpression addCollectToListIfNeed(PsiExpression expression, PsiType type, PsiElementFactory elementFactory) { - if (type instanceof PsiClassType) { - PsiClass resolvedParamClass = ((PsiClassType)type).resolve(); - if (resolvedParamClass != null && CommonClassNames.JAVA_LANG_ITERABLE.equals(resolvedParamClass.getQualifiedName())) { - final PsiExpression newExpression = - elementFactory.createExpressionFromText(expression.getText() + ".collect(java.util.stream.Collectors.toList())", null); - return (PsiExpression) expression.replace(newExpression); - } + return addCollectToListIfNeed(expression, returnType); } return expression; } + private PsiExpression addCollectToListIfNeed(PsiExpression expression, PsiType type) { + if (type instanceof PsiClassType && ((PsiClassType)type).rawType().equalsToText(CommonClassNames.JAVA_LANG_ITERABLE)) { + return (PsiExpression)addCollectionToList(expression); + } + return expression; + } + + private PsiElement addCollectionToList(PsiElement expression) { + return expression.replace(myElementFactory.createExpressionFromText(expression.getText() + ".collect(java.util.stream.Collectors.toList())", null)); + } + private static boolean isIterableMethodParameter(PsiElement listExpression, PsiExpression parameterExpression) { if (!(listExpression instanceof PsiExpressionList)) { return false; @@ -172,28 +167,38 @@ public class GuavaFluentIterableMethodConverters { return false; } final Project project = parameterExpression.getProject(); - final PsiClass fluentIterable = JavaPsiFacade.getInstance(project) - .findClass(GuavaFluentIterableInspection.GUAVA_FLUENT_ITERABLE, GlobalSearchScope.allScope(project)); + final PsiClass fluentIterable = JavaPsiFacade.getInstance(project).findClass(GuavaFluentIterableInspection.GUAVA_FLUENT_ITERABLE, + listExpression.getResolveScope()); return GuavaFluentIterableInspection.isMethodWithParamAcceptsConversion((PsiMethodCallExpression)listExpression.getParent(), parameterExpression, fluentIterable); } @Nullable - private static PsiMethodCallExpression convertMethodCallDeep(PsiElementFactory elementFactory, - @NotNull PsiMethodCallExpression methodCall) { + private PsiMethodCallExpression convertMethodCallDeep(@NotNull PsiMethodCallExpression methodCall) { PsiMethodCallExpression newMethodCall = methodCall; PsiMethodCallExpression returnCall = null; while (true) { - final Pair converted = convertMethodCall(elementFactory, newMethodCall); - if (converted.getSecond()) { + final PsiReferenceExpression methodExpression = newMethodCall.getMethodExpression(); + final String name = methodExpression.getReferenceName(); + PsiMethodCallExpression converted = convertFromMethodCall(newMethodCall); + if (converted != null) { return returnCall; } + if (TO_OTHER_COLLECTION_METHODS.containsKey(name)) { + converted = convertToCollection(newMethodCall, name); + } + else { + final FluentIterableMethodTransformer transformer = METHOD_INDEX.get(name); + LOG.assertTrue(transformer != null, name); + converted = transformer.transform(newMethodCall, myElementFactory); + } + if (converted == null) { + return returnCall; + } + if (returnCall == null) { - returnCall = converted.getFirst(); + returnCall = converted; } - if (converted.getFirst() == null) { - return returnCall; - } - newMethodCall = converted.getFirst(); + newMethodCall = converted; final PsiExpression expression = newMethodCall.getMethodExpression().getQualifierExpression(); if (expression instanceof PsiMethodCallExpression) { newMethodCall = (PsiMethodCallExpression)expression; @@ -203,42 +208,30 @@ public class GuavaFluentIterableMethodConverters { } } } - - public static Pair convertMethodCall(PsiElementFactory elementFactory, PsiMethodCallExpression methodCall) { - final PsiReferenceExpression methodExpression = methodCall.getMethodExpression(); - final String name = methodExpression.getReferenceName(); - if (TO_OTHER_COLLECTION_METHODS.containsKey(name)) { - return Pair.create(convertToCollection(methodCall, name, elementFactory), false); - } - else if (GuavaFluentIterableInspection.FLUENT_ITERABLE_FROM.equals(name)) { + + private PsiMethodCallExpression convertFromMethodCall(PsiMethodCallExpression methodCall) { + if (GuavaFluentIterableInspection.FLUENT_ITERABLE_FROM.equals(methodCall.getMethodExpression().getReferenceName())) { final PsiExpression[] argumentList = methodCall.getArgumentList().getExpressions(); LOG.assertTrue(argumentList.length == 1); final PsiExpression expression = argumentList[0]; final PsiType type = expression.getType(); LOG.assertTrue(type instanceof PsiClassType); - final PsiClass resolvedClass = ((PsiClassType)type).resolve(); final String newExpressionText; - if (InheritanceUtil.isInheritor(resolvedClass, CommonClassNames.JAVA_UTIL_COLLECTION)) { + if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_COLLECTION)) { newExpressionText = expression.getText() + ".stream()"; } else { newExpressionText = "java.util.stream.StreamSupport.stream(" + expression.getText() + ".spliterator(), false)"; } - return Pair.create((PsiMethodCallExpression)methodCall.replace(elementFactory.createExpressionFromText(newExpressionText, null)), true); - } - else { - final FluentIterableMethodTransformer transformer = METHOD_INDEX.get(name); - LOG.assertTrue(transformer != null, name); - final PsiMethodCallExpression transformedExpression = transformer.transform(methodCall, elementFactory); - return Pair.create(transformedExpression, false); + return (PsiMethodCallExpression)methodCall.replace(myElementFactory.createExpressionFromText(newExpressionText, null)); } + return null; } - private static PsiMethodCallExpression convertToCollection(final PsiMethodCallExpression methodCall, - final String methodName, - final PsiElementFactory elementFactory) { + private PsiMethodCallExpression convertToCollection(final PsiMethodCallExpression methodCall, + final String methodName) { final PsiExpression[] expressions = methodCall.getArgumentList().getExpressions(); - assert expressions.length < 2; + LOG.assertTrue(expressions.length < 2); String template = TO_OTHER_COLLECTION_METHODS.get(methodName); if (expressions.length == 1) { template = String.format(template, expressions[0].getText()); @@ -247,8 +240,7 @@ public class GuavaFluentIterableMethodConverters { if (qualifier == null) { return null; } - final String text = qualifier.getText() + "." + template; - final PsiExpression expression = elementFactory.createExpressionFromText(text, null); + final PsiExpression expression = myElementFactory.createExpressionFromText(qualifier.getText() + "." + template, null); return (PsiMethodCallExpression)methodCall.replace(expression); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java index 838032b0638e..25c7ebe0d16f 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java @@ -22,7 +22,6 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.PsiDiamondTypeUtil; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.*; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; @@ -156,16 +155,17 @@ class PseudoLambdaReplaceTemplate { return null; } - final PsiMethod method = expression.resolveMethod(); - if (method == null) { + final JavaResolveResult result = expression.getMethodExpression().advancedResolve(false); + final PsiElement element = result.getElement(); + if (!(element instanceof PsiMethod)) { return null; } - final PsiParameter[] expectedParameters = method.getParameterList().getParameters(); + final PsiParameter[] expectedParameters = ((PsiMethod)element).getParameterList().getParameters(); if (argumentTypes.length != expectedParameters.length) { return null; } - final JavaResolveResult result = expression.getMethodExpression().advancedResolve(false); + final PsiSubstitutor methodSubstitutor = result.getSubstitutor(); return validate(argumentTypes, methodReturnType, methodSubstitutor, expression); } @@ -202,6 +202,9 @@ class PseudoLambdaReplaceTemplate { for (int i = 0; i < arguments.length; i++) { PsiType type = arguments[i]; + if (type == null) { + return null; + } if (isFunction(type, methodReturnType, methodSubstitutor, context)) { if (lambdaPosition == -1) { lambdaPosition = i; @@ -357,8 +360,7 @@ class PseudoLambdaReplaceTemplate { } @NotNull - public PsiExpression convertToStream(final PsiMethodCallExpression expression, PsiMethod method, boolean force) { - LOG.assertTrue(expression != null); + public PsiExpression convertToStream(@NotNull final PsiMethodCallExpression expression, @Nullable PsiMethod method, boolean force) { if (method == null) { method = expression.resolveMethod(); if (method == null) { @@ -489,7 +491,16 @@ class PseudoLambdaReplaceTemplate { LOG.assertTrue(method != null); return JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText(expression.getText() + "::" + method.getName(), null); } - return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true); + + final PsiType psiType = expression.getType(); + if (psiType != null) { + PsiNewExpression newArrayExpression = (PsiNewExpression)JavaPsiFacade.getElementFactory(expression.getProject()) + .createExpressionFromText("new " + psiType.getCanonicalText() + "[]{" + expression.getText() + "}", expression); + PsiArrayInitializerExpression initializer = newArrayExpression.getArrayInitializer(); + LOG.assertTrue(initializer != null); + return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(initializer.getInitializers()[0], true); + } + return null; } @NotNull @@ -547,15 +558,9 @@ class PseudoLambdaReplaceTemplate { private static boolean isIterableOrArray(final PsiType type) { if (type instanceof PsiClassType) { - final PsiClass resolvedClass = ((PsiClassType)type).resolve(); - if (resolvedClass != null) { - return InheritanceUtil.isInheritor(resolvedClass, CommonClassNames.JAVA_LANG_ITERABLE); - } + return InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ITERABLE); } - else if (type instanceof PsiArrayType) { - return true; - } - return false; + return type instanceof PsiArrayType; } @Override diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodInspection.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodInspection.java index 398aabc0ff20..e08c66fa943b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodInspection.java @@ -15,6 +15,8 @@ */ package com.intellij.codeInspection.java18StreamApi; +import com.intellij.codeInsight.CodeInsightUtil; +import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInspection.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; @@ -61,47 +63,46 @@ public class StaticPseudoFunctionalStyleMethodInspection extends BaseJavaBatchLo if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) { return PsiElementVisitor.EMPTY_VISITOR; } - return new PsiElementVisitor() { + return new JavaElementVisitor() { @Override - public void visitElement(PsiElement element) { - if (element instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element; - String qName = methodCallExpression.getMethodExpression().getQualifiedName(); - if (qName == null) { - return; - } - qName = StringUtil.getShortName(qName); - final Collection handlerInfos = myOptions.findElementsByMethodName(qName); - if (handlerInfos.isEmpty()) { - return; - } - final PsiMethod method = methodCallExpression.resolveMethod(); - if (method == null) { - return; - } - final PsiClass aClass = method.getContainingClass(); - if (aClass == null) { - return; - } - final String classQualifiedName = aClass.getQualifiedName(); - if (classQualifiedName == null) { - return; - } - StaticPseudoFunctionalStyleMethodOptions.PipelineElement suitableHandler = null; - for (StaticPseudoFunctionalStyleMethodOptions.PipelineElement h : handlerInfos) { - if (h.getHandlerClass().equals(classQualifiedName)) { - suitableHandler = h; - break; - } - } - if (suitableHandler == null) { - return; - } - final PseudoLambdaReplaceTemplate.ValidationInfo validationInfo = suitableHandler.getTemplate().validate(methodCallExpression); - if (validationInfo != null) { - holder.registerProblem(methodCallExpression.getMethodExpression(), "Pseudo functional style code", new ReplacePseudoLambdaWithLambda(suitableHandler)); + public void visitMethodCallExpression(PsiMethodCallExpression methodCallExpression) { + String qName = methodCallExpression.getMethodExpression().getQualifiedName(); + if (qName == null) { + return; + } + qName = StringUtil.getShortName(qName); + final Collection handlerInfos = myOptions.findElementsByMethodName(qName); + if (handlerInfos.isEmpty()) { + return; + } + final PsiMethod method = methodCallExpression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass aClass = method.getContainingClass(); + if (aClass == null) { + return; + } + final String classQualifiedName = aClass.getQualifiedName(); + if (classQualifiedName == null) { + return; + } + StaticPseudoFunctionalStyleMethodOptions.PipelineElement suitableHandler = null; + for (StaticPseudoFunctionalStyleMethodOptions.PipelineElement h : handlerInfos) { + if (h.getHandlerClass().equals(classQualifiedName)) { + suitableHandler = h; + break; } } + if (suitableHandler == null) { + return; + } + final PseudoLambdaReplaceTemplate.ValidationInfo validationInfo = suitableHandler.getTemplate().validate(methodCallExpression); + if (validationInfo != null) { + holder.registerProblem(methodCallExpression.getMethodExpression(), + "Pseudo functional style code", + new ReplacePseudoLambdaWithLambda(suitableHandler)); + } } }; } @@ -128,9 +129,16 @@ public class StaticPseudoFunctionalStyleMethodInspection extends BaseJavaBatchLo @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiMethodCallExpression expression = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class); - LOG.assertTrue(expression != null); - myHandler.getTemplate().convertToStream(expression, null, false); + final PsiElement psiElement = descriptor.getPsiElement(); + if (!FileModificationService.getInstance().preparePsiElementsForWrite(psiElement)) { + return; + } + if (psiElement instanceof PsiReferenceExpression) { + PsiElement parent = psiElement.getParent(); + if (parent instanceof PsiMethodCallExpression) { + myHandler.getTemplate().convertToStream((PsiMethodCallExpression)parent, null, false); + } + } } } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/openapi/roots/ProjectModelModificationService.java b/java/java-impl/src/com/intellij/openapi/roots/ProjectModelModificationService.java index 357186c5ac90..86ce60e047e7 100644 --- a/java/java-impl/src/com/intellij/openapi/roots/ProjectModelModificationService.java +++ b/java/java-impl/src/com/intellij/openapi/roots/ProjectModelModificationService.java @@ -31,6 +31,8 @@ import java.util.Collections; * project model. Since importing the changes to IDEA's project model may take a while the method work asynchronously and returns * {@link Promise} objects which may be used to be notified when the project configuration is finally updated. * + * @see ProjectModelModifier + * * @author nik */ public abstract class ProjectModelModificationService { diff --git a/java/java-impl/src/com/intellij/openapi/roots/ProjectModelModifier.java b/java/java-impl/src/com/intellij/openapi/roots/ProjectModelModifier.java index cc324064f529..d4b0e71e16dc 100644 --- a/java/java-impl/src/com/intellij/openapi/roots/ProjectModelModifier.java +++ b/java/java-impl/src/com/intellij/openapi/roots/ProjectModelModifier.java @@ -26,6 +26,10 @@ import java.util.Collection; /** * Register implementation of this extension to support custom dependency management system for {@link ProjectModelModificationService}. + * The default implementation which modify IDEA's project model directly is registered as the last extension so it'll be executed if all other + * extensions refuse to handle modification by returning {@code null}. + * + * @see ProjectModelModificationService * * @author nik */ diff --git a/java/java-impl/src/com/intellij/psi/filters/ElementExtractorFilter.java b/java/java-impl/src/com/intellij/psi/filters/ElementExtractorFilter.java index 3e29bccb5394..2c930edaf3ba 100644 --- a/java/java-impl/src/com/intellij/psi/filters/ElementExtractorFilter.java +++ b/java/java-impl/src/com/intellij/psi/filters/ElementExtractorFilter.java @@ -26,18 +26,12 @@ import com.intellij.psi.infos.CandidateInfo; * To change this template use Options | File Templates. */ public class ElementExtractorFilter implements ElementFilter{ - ElementFilter myFilter; - - public ElementExtractorFilter(){} + private final ElementFilter myFilter; public ElementExtractorFilter(ElementFilter filter){ myFilter = filter; } - public void setFilter(ElementFilter filter){ - myFilter = filter; - } - public ElementFilter getFilter(){ return myFilter; } diff --git a/java/java-impl/src/com/intellij/psi/filters/classes/ClassAssignableFilter.java b/java/java-impl/src/com/intellij/psi/filters/classes/ClassAssignableFilter.java deleted file mode 100644 index 5034c48c0710..000000000000 --- a/java/java-impl/src/com/intellij/psi/filters/classes/ClassAssignableFilter.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.filters.classes; - -import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiManager; -import com.intellij.psi.filters.ElementFilter; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.reference.SoftReference; -import com.intellij.util.ReflectionUtil; - -/** - * Created by IntelliJ IDEA. - * User: ik - * Date: 26.03.2003 - * Time: 21:01:47 - * To change this template use Options | File Templates. - */ -public abstract class ClassAssignableFilter implements ElementFilter{ - protected String myClassName = null; - protected PsiClass myClass = null; - private SoftReference myCachedClass = new SoftReference(null); - - @Override - public abstract boolean isAcceptable(Object aClass, PsiElement context); - public abstract String toString(); - - @Override - public boolean isClassAcceptable(Class hintClass){ - return ReflectionUtil.isAssignable(PsiClass.class, hintClass); - } - - protected PsiClass getPsiClass(PsiManager manager, GlobalSearchScope scope){ - if(myClass != null){ - return myClass; - } - - if(myCachedClass.get() == null && manager != null){ - myCachedClass = new SoftReference(JavaPsiFacade.getInstance(manager.getProject()).findClass(myClassName, scope)); - } - return (PsiClass) myCachedClass.get(); - } - -} diff --git a/java/java-impl/src/com/intellij/psi/filters/classes/EnumOrAnnotationTypeFilter.java b/java/java-impl/src/com/intellij/psi/filters/classes/EnumOrAnnotationTypeFilter.java deleted file mode 100644 index de48dfa83836..000000000000 --- a/java/java-impl/src/com/intellij/psi/filters/classes/EnumOrAnnotationTypeFilter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.filters.classes; - -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; -import com.intellij.psi.filters.ElementFilter; -import com.intellij.util.ReflectionUtil; -import org.jetbrains.annotations.NonNls; - -public class EnumOrAnnotationTypeFilter implements ElementFilter{ - - @Override - public boolean isClassAcceptable(Class hintClass){ - return ReflectionUtil.isAssignable(PsiClass.class, hintClass); - } - - @Override - public boolean isAcceptable(Object element, PsiElement context){ - if(element instanceof PsiClass){ - return ((PsiClass)element).isEnum() || ((PsiClass)element).isAnnotationType(); - } - return false; - } - - @NonNls - public String toString(){ - return "enum or annotation type"; - } -} diff --git a/java/java-impl/src/com/intellij/psi/filters/classes/InterfaceFilter.java b/java/java-impl/src/com/intellij/psi/filters/classes/InterfaceFilter.java deleted file mode 100644 index 702dced21a3a..000000000000 --- a/java/java-impl/src/com/intellij/psi/filters/classes/InterfaceFilter.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.filters.classes; - -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; -import com.intellij.psi.filters.ElementFilter; -import com.intellij.util.ReflectionUtil; - -/** - * Created by IntelliJ IDEA. - * User: ik - * Date: 28.01.2003 - * Time: 21:00:45 - * To change this template use Options | File Templates. - */ -public class InterfaceFilter implements ElementFilter{ - @Override - public boolean isClassAcceptable(Class hintClass){ - return ReflectionUtil.isAssignable(PsiClass.class, hintClass); - } - - @Override - public boolean isAcceptable(Object element, PsiElement context){ - return element instanceof PsiClass && ((PsiClass)element).isInterface(); - } - - public String toString(){ - return "interface"; - } -} diff --git a/java/java-impl/src/com/intellij/psi/filters/getters/FilterGetter.java b/java/java-impl/src/com/intellij/psi/filters/getters/FilterGetter.java deleted file mode 100644 index adde94debdae..000000000000 --- a/java/java-impl/src/com/intellij/psi/filters/getters/FilterGetter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.filters.getters; - -import com.intellij.codeInsight.completion.CompletionContext; -import com.intellij.psi.PsiElement; -import com.intellij.psi.filters.ContextGetter; -import com.intellij.psi.filters.ElementFilter; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by IntelliJ IDEA. - * User: ik - * Date: 15.04.2003 - * Time: 17:18:58 - * To change this template use Options | File Templates. - */ -public class FilterGetter implements ContextGetter{ - private final ContextGetter myBaseGetter; - private final ElementFilter myFilter; - - public FilterGetter(ContextGetter baseGetter, ElementFilter filter){ - myBaseGetter = baseGetter; - myFilter = filter; - } - - @Override - public Object[] get(PsiElement context, CompletionContext completionContext){ - final List results = new ArrayList(); - final Object[] elements = myBaseGetter.get(context, completionContext); - for (final Object element : elements) { - if (myFilter.isClassAcceptable(element.getClass()) && myFilter.isAcceptable(element, context)) { - results.add(element); - } - } - return results.toArray(); - } -} diff --git a/java/java-impl/src/com/intellij/psi/filters/getters/ThisGetter.java b/java/java-impl/src/com/intellij/psi/filters/getters/ThisGetter.java index bf8c1766d1ec..6780e2adcb7c 100644 --- a/java/java-impl/src/com/intellij/psi/filters/getters/ThisGetter.java +++ b/java/java-impl/src/com/intellij/psi/filters/getters/ThisGetter.java @@ -15,9 +15,7 @@ */ package com.intellij.psi.filters.getters; -import com.intellij.codeInsight.completion.CompletionContext; import com.intellij.psi.*; -import com.intellij.psi.filters.ContextGetter; import com.intellij.util.IncorrectOperationException; import java.util.ArrayList; @@ -30,11 +28,7 @@ import java.util.List; * Time: 14:02:59 * To change this template use Options | File Templates. */ -public class ThisGetter implements ContextGetter{ - @Override - public Object[] get(PsiElement context, CompletionContext completionContext) { - return getThisExpressionVariants(context).toArray(); - } +public class ThisGetter { public static List getThisExpressionVariants(PsiElement context) { boolean first = true; diff --git a/java/java-impl/src/com/intellij/psi/filters/position/BeforeElementFilter.java b/java/java-impl/src/com/intellij/psi/filters/position/BeforeElementFilter.java deleted file mode 100644 index 5c10e2249808..000000000000 --- a/java/java-impl/src/com/intellij/psi/filters/position/BeforeElementFilter.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.filters.position; - -import com.intellij.psi.PsiElement; -import com.intellij.psi.filters.ElementFilter; - -/** - * Created by IntelliJ IDEA. - * User: ik - * Date: 03.02.2003 - * Time: 18:29:13 - * To change this template use Options | File Templates. - */ -public class BeforeElementFilter extends PositionElementFilter{ - public BeforeElementFilter(ElementFilter filter){ - setFilter(filter); - } - - public BeforeElementFilter(){} - @Override - public boolean isAcceptable(Object element, PsiElement scope){ - if (!(element instanceof PsiElement)) return false; - final PsiElement ownerChild = getOwnerChild(scope, (PsiElement) element); - if(ownerChild == null) return false; - PsiElement currentChild = ownerChild.getNextSibling(); - while(currentChild != null){ - if(getFilter().isAcceptable(currentChild, scope)){ - return true; - } - currentChild = currentChild.getNextSibling(); - } - return false; - } - - public String toString(){ - return "before(" + getFilter().toString() + ")"; - } -} diff --git a/java/java-impl/src/com/intellij/psi/filters/position/InsideElementFilter.java b/java/java-impl/src/com/intellij/psi/filters/position/InsideElementFilter.java deleted file mode 100644 index 74dde269e34c..000000000000 --- a/java/java-impl/src/com/intellij/psi/filters/position/InsideElementFilter.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.filters.position; - -import com.intellij.psi.PsiElement; -import com.intellij.psi.filters.ElementFilter; - -/** - * Created by IntelliJ IDEA. - * User: ik - * Date: 25.03.2003 - * Time: 12:11:40 - * To change this template use Options | File Templates. - */ -public class InsideElementFilter extends PositionElementFilter{ - public InsideElementFilter(ElementFilter filter){ - setFilter(filter); - } - - public InsideElementFilter(){} - - @Override - public boolean isAcceptable(Object element, PsiElement scope){ - if (!(element instanceof PsiElement)) return false; - PsiElement currentChild = getOwnerChild(scope, (PsiElement) element); - return getFilter().isAcceptable(currentChild, scope); - } - - public String toString(){ - return "in(" + getFilter().toString() + ")"; - } -} diff --git a/java/java-impl/src/com/intellij/psi/filters/position/TokenTypeFilter.java b/java/java-impl/src/com/intellij/psi/filters/position/TokenTypeFilter.java index 58d878840248..9232f1a4c254 100644 --- a/java/java-impl/src/com/intellij/psi/filters/position/TokenTypeFilter.java +++ b/java/java-impl/src/com/intellij/psi/filters/position/TokenTypeFilter.java @@ -31,9 +31,7 @@ import com.intellij.util.ReflectionUtil; * To change this template use Options | File Templates. */ public class TokenTypeFilter implements ElementFilter{ - private IElementType myType = null; - - public TokenTypeFilter(){} + private final IElementType myType; public TokenTypeFilter(IElementType type){ myType = type; diff --git a/java/java-impl/src/com/intellij/psi/filters/types/AssignableFromFilter.java b/java/java-impl/src/com/intellij/psi/filters/types/AssignableFromFilter.java index b2f7611ff673..e791c926fd58 100644 --- a/java/java-impl/src/com/intellij/psi/filters/types/AssignableFromFilter.java +++ b/java/java-impl/src/com/intellij/psi/filters/types/AssignableFromFilter.java @@ -42,8 +42,6 @@ public class AssignableFromFilter implements ElementFilter{ myClassName = className; } - public AssignableFromFilter(){} - @Override public boolean isClassAcceptable(Class hintClass){ return true; diff --git a/java/java-impl/src/com/intellij/psi/filters/types/AssignableGroupFilter.java b/java/java-impl/src/com/intellij/psi/filters/types/AssignableGroupFilter.java index 56fa73fb599c..ffbef70e7c50 100644 --- a/java/java-impl/src/com/intellij/psi/filters/types/AssignableGroupFilter.java +++ b/java/java-impl/src/com/intellij/psi/filters/types/AssignableGroupFilter.java @@ -31,11 +31,6 @@ import com.intellij.psi.filters.OrFilter; * To change this template use Options | File Templates. */ public class AssignableGroupFilter extends OrFilter implements InitializableFilter{ - public AssignableGroupFilter(){} - - public AssignableGroupFilter(PsiClass[] classes){ - init(classes); - } @Override public void init(Object[] classes){ diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java index d4f1a12ccba8..336c549861a8 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java @@ -69,6 +69,23 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo private Map myPreferredWraps; private AbstractJavaBlock myParentBlock; + private BlockFactory myBlockFactory = new BlockFactory() { + @Override + public Block createBlock(ASTNode node, Indent indent, Alignment alignment, Wrap wrap) { + return new SimpleJavaBlock(node, wrap, AlignmentStrategy.wrap(alignment), indent, mySettings, myJavaSettings); + } + + @Override + public CommonCodeStyleSettings getSettings() { + return mySettings; + } + + @Override + public JavaCodeStyleSettings getJavaSettings() { + return myJavaSettings; + } + }; + protected AbstractJavaBlock(@NotNull final ASTNode node, final Wrap wrap, final Alignment alignment, @@ -476,12 +493,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo if (nodeType == JavaElementType.POLYADIC_EXPRESSION) nodeType = JavaElementType.BINARY_EXPRESSION; if (childType == JavaTokenType.LBRACE && nodeType == JavaElementType.ARRAY_INITIALIZER_EXPRESSION) { - final Wrap wrap = Wrap.createWrap(getWrapType(mySettings.ARRAY_INITIALIZER_WRAP), false); - child = processParenthesisBlock(JavaTokenType.LBRACE, JavaTokenType.RBRACE, - result, - child, - WrappingStrategy.createDoNotWrapCommaStrategy(wrap), - mySettings.ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION); + ArrayInitializerBlocksBuilder builder = new ArrayInitializerBlocksBuilder(myNode, myBlockFactory); + List newlyCreated = builder.buildBlocks(); + + child = myNode.getLastChildNode(); + result.addAll(newlyCreated); } else if (childType == JavaTokenType.LBRACE && nodeType == JavaElementType.ANNOTATION_ARRAY_INITIALIZER) { final Wrap wrap = Wrap.createWrap(getWrapType(mySettings.ARRAY_INITIALIZER_WRAP), false); @@ -514,10 +530,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo mySettings.ALIGN_MULTILINE_RESOURCES); } else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.ANNOTATION_PARAMETER_LIST) { - Wrap wrap = Wrap.createWrap(getWrapType(myJavaSettings.ANNOTATION_PARAMETER_WRAP), false); - child = processParenthesisBlock(result, child, - WrappingStrategy.createDoNotWrapCommaStrategy(wrap), - myJavaSettings.ALIGN_MULTILINE_ANNOTATION_PARAMETERS); + AnnotationInitializerBlocksBuilder builder = new AnnotationInitializerBlocksBuilder(myNode, myBlockFactory); + List newlyCreated = builder.buildBlocks(); + + child = myNode.getLastChildNode(); + result.addAll(newlyCreated); } else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.PARENTH_EXPRESSION) { child = processParenthesisBlock(result, child, diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/AnnotationInitializerBlocksBuilder.java b/java/java-impl/src/com/intellij/psi/formatter/java/AnnotationInitializerBlocksBuilder.java new file mode 100644 index 000000000000..19218dea7f35 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/formatter/java/AnnotationInitializerBlocksBuilder.java @@ -0,0 +1,75 @@ +/* + * 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.formatter.java; + +import com.intellij.formatting.Alignment; +import com.intellij.formatting.Block; +import com.intellij.formatting.Indent; +import com.intellij.formatting.Wrap; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Condition; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNameValuePair; +import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.codeStyle.JavaCodeStyleSettings; +import com.intellij.psi.util.PsiTreeUtil; + +import java.util.List; + +import static com.intellij.psi.formatter.java.JavaFormatterUtil.getWrapType; + +public class AnnotationInitializerBlocksBuilder { + + private final BlockFactory myFactory; + private final ASTNode myNode; + private final JavaCodeStyleSettings myJavaSettings; + + public AnnotationInitializerBlocksBuilder(ASTNode node, BlockFactory factory) { + myNode = node; + myFactory = factory; + myJavaSettings = myFactory.getJavaSettings(); + } + + public List buildBlocks() { + final Wrap wrap = Wrap.createWrap(getWrapType(myJavaSettings.ANNOTATION_PARAMETER_WRAP), false); + final Alignment alignment = myJavaSettings.ALIGN_MULTILINE_ANNOTATION_PARAMETERS ? Alignment.createAlignment() : null; + + ChildrenBlocksBuilder.Config config = new ChildrenBlocksBuilder.Config() + .setDefaultIndent(Indent.getContinuationWithoutFirstIndent()) + .setIndent(JavaTokenType.RPARENTH, Indent.getNoneIndent()) + .setIndent(JavaTokenType.LPARENTH, Indent.getNoneIndent()) + + .setDefaultWrap(wrap) + .setNoWrap(JavaTokenType.COMMA) + .setNoWrap(JavaTokenType.RPARENTH) + .setNoWrap(JavaTokenType.LPARENTH) + + .setDefaultAlignment(alignment) + .setNoAlignment(JavaTokenType.COMMA) + .setNoAlignment(JavaTokenType.LPARENTH) + .setNoAlignmentIf(JavaTokenType.RPARENTH, new Condition() { + @Override + public boolean value(ASTNode node) { + PsiElement prev = PsiTreeUtil.skipSiblingsBackward(node.getPsi(), PsiWhiteSpace.class); + if (prev == null) return false; + return prev instanceof PsiNameValuePair && !PsiTreeUtil.hasErrorElements(prev); + } + }); + + return config.createBuilder().buildNodeChildBlocks(myNode, myFactory); + } +} diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/ArrayInitializerBlocksBuilder.java b/java/java-impl/src/com/intellij/psi/formatter/java/ArrayInitializerBlocksBuilder.java new file mode 100644 index 000000000000..3a04e012728d --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/formatter/java/ArrayInitializerBlocksBuilder.java @@ -0,0 +1,73 @@ +/* + * 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.formatter.java; + +import com.intellij.formatting.Alignment; +import com.intellij.formatting.Block; +import com.intellij.formatting.Indent; +import com.intellij.formatting.Wrap; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Condition; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.util.PsiTreeUtil; + +import java.util.List; + +import static com.intellij.psi.formatter.java.JavaFormatterUtil.getWrapType; + +public class ArrayInitializerBlocksBuilder { + private final ASTNode myNode; + private final BlockFactory myBlockFactory; + private final CommonCodeStyleSettings mySettings; + + public ArrayInitializerBlocksBuilder(ASTNode node, BlockFactory blockFactory) { + myNode = node; + myBlockFactory = blockFactory; + mySettings = myBlockFactory.getSettings(); + } + + public List buildBlocks() { + Wrap wrap = Wrap.createWrap(getWrapType(mySettings.ARRAY_INITIALIZER_WRAP), false); + Alignment alignment = mySettings.ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION ? Alignment.createAlignment() : null; + + ChildrenBlocksBuilder.Config config = new ChildrenBlocksBuilder.Config() + .setDefaultIndent(Indent.getContinuationWithoutFirstIndent()) + .setIndent(JavaTokenType.RBRACE, Indent.getNoneIndent()) + .setIndent(JavaTokenType.LBRACE, Indent.getNoneIndent()) + + .setDefaultWrap(wrap) + .setNoWrap(JavaTokenType.COMMA) + .setNoWrap(JavaTokenType.RBRACE) + .setNoWrap(JavaTokenType.LBRACE) + + .setDefaultAlignment(alignment) + .setNoAlignment(JavaTokenType.COMMA) + .setNoAlignment(JavaTokenType.LBRACE) + .setNoAlignmentIf(JavaTokenType.RBRACE, new Condition() { + @Override + public boolean value(ASTNode node) { + PsiElement prev = PsiTreeUtil.skipSiblingsBackward(node.getPsi(), PsiWhiteSpace.class); + if (prev == null) return false; + return prev.getNode().getElementType() != JavaTokenType.COMMA; + } + }); + + return config.createBuilder().buildNodeChildBlocks(myNode, myBlockFactory); + } +} diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java index 3ba6ef5e263b..9c1da188c90e 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java @@ -17,9 +17,7 @@ package com.intellij.psi.formatter.java; import com.intellij.formatting.*; import com.intellij.lang.ASTNode; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiStatement; -import com.intellij.psi.PsiTypeElement; +import com.intellij.psi.*; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.codeStyle.JavaCodeStyleSettings; import com.intellij.psi.formatter.FormatterUtil; @@ -255,8 +253,8 @@ public class BlockContainingJavaBlock extends AbstractJavaBlock{ if (prevBlock instanceof ASTBlock && nextBlock instanceof ASTBlock) { ASTNode prevNode = ((ASTBlock)prevBlock).getNode(); ASTNode nextNode = ((ASTBlock)nextBlock).getNode(); - if (prevNode != null && nextNode != null && prevNode.getElementType() == JavaTokenType.RPARENTH - && nextNode.getElementType() != JavaTokenType.LBRACE) + if (prevNode != null && nextNode != null && prevNode.getElementType() == JavaTokenType.RPARENTH + && nextNode.getElementType() != JavaTokenType.LBRACE) { useExternalIndent = true; } @@ -267,7 +265,7 @@ public class BlockContainingJavaBlock extends AbstractJavaBlock{ return new ChildAttributes(getCodeBlockChildExternalIndent(newChildIndex), null); } else { - return new ChildAttributes(myIndentsBefore.get(newChildIndex), null); + return new ChildAttributes(myIndentsBefore.get(newChildIndex), getUsedAlignment(newChildIndex)); } } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/BlockFactory.java b/java/java-impl/src/com/intellij/psi/formatter/java/BlockFactory.java new file mode 100644 index 000000000000..3069d2339065 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/formatter/java/BlockFactory.java @@ -0,0 +1,34 @@ +/* + * 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.formatter.java; + +import com.intellij.formatting.Alignment; +import com.intellij.formatting.Block; +import com.intellij.formatting.Indent; +import com.intellij.formatting.Wrap; +import com.intellij.lang.ASTNode; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.codeStyle.JavaCodeStyleSettings; + +public interface BlockFactory { + + Block createBlock(ASTNode node, Indent indent, Alignment alignment, Wrap wrap); + + CommonCodeStyleSettings getSettings(); + + JavaCodeStyleSettings getJavaSettings(); + +} diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/ChildrenBlocksBuilder.java b/java/java-impl/src/com/intellij/psi/formatter/java/ChildrenBlocksBuilder.java new file mode 100644 index 000000000000..4d7d5e8bbb2a --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/formatter/java/ChildrenBlocksBuilder.java @@ -0,0 +1,144 @@ +/* + * 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.formatter.java; + +import com.intellij.formatting.Alignment; +import com.intellij.formatting.Block; +import com.intellij.formatting.Indent; +import com.intellij.formatting.Wrap; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Condition; +import com.intellij.psi.formatter.FormatterUtil; +import com.intellij.psi.tree.IElementType; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +public class ChildrenBlocksBuilder { + private Config myConfig; + + private ChildrenBlocksBuilder(Config builder) { + myConfig = builder; + } + + public List buildNodeChildBlocks(ASTNode node, BlockFactory factory) { + List blocks = ContainerUtil.newArrayList(); + + for (ASTNode child : node.getChildren(null)) { + if (FormatterUtil.isWhitespaceOrEmpty(child) || child.getTextLength() == 0) { + continue; + } + + Alignment alignment = myConfig.getAlignment(child); + + IElementType type = child.getElementType(); + Indent indent = myConfig.getIndent(type); + Wrap wrap = myConfig.getWrap(type); + + blocks.add(factory.createBlock(child, indent, alignment, wrap)); + } + + return blocks; + } + + public static class Config { + private static final Alignment NO_ALIGNMENT = Alignment.createAlignment(); + private static final Wrap NO_WRAP = Wrap.createWrap(0, false); + + private Map myAlignments = ContainerUtil.newHashMap(); + private Map myIndents = ContainerUtil.newHashMap(); + private Map myWraps = ContainerUtil.newHashMap(); + + private Map> myNoneAlignmentCondition = ContainerUtil.newHashMap(); + + private Alignment myDefaultAlignment; + private Indent myDefaultIndent; + private Wrap myDefaultWrap; + + public ChildrenBlocksBuilder createBuilder() { + return new ChildrenBlocksBuilder(this); + } + + public Config setDefaultAlignment(Alignment alignment) { + myDefaultAlignment = alignment; + return this; + } + + public Config setDefaultWrap(Wrap wrap) { + myDefaultWrap = wrap; + return this; + } + + public Config setDefaultIndent(Indent indent) { + myDefaultIndent = indent; + return this; + } + + public Config setAlignment(@NotNull IElementType elementType, @NotNull Alignment alignment) { + myAlignments.put(elementType, alignment); + return this; + } + + public Config setNoAlignment(IElementType elementType) { + myAlignments.put(elementType, NO_ALIGNMENT); + return this; + } + + public Config setNoAlignmentIf(IElementType elementType, Condition applyAlignCondition) { + myNoneAlignmentCondition.put(elementType, applyAlignCondition); + return this; + } + + public Config setIndent(IElementType elementType, Indent indent) { + myIndents.put(elementType, indent); + return this; + } + + private Indent getIndent(IElementType elementType) { + Indent indent = myIndents.get(elementType); + return indent != null ? indent : myDefaultIndent; + } + + private Alignment getAlignment(ASTNode node) { + IElementType elementType = node.getElementType(); + + Condition noneAlignmentCondition = myNoneAlignmentCondition.get(elementType); + if (noneAlignmentCondition != null && noneAlignmentCondition.value(node)) { + return null; + } + + Alignment alignment = myAlignments.get(elementType); + if (alignment == null) { + return myDefaultAlignment; + } + return alignment == NO_ALIGNMENT ? null : alignment; + } + + private Wrap getWrap(IElementType elementType) { + Wrap wrap = myWraps.get(elementType); + if (wrap == NO_WRAP) return null; + return wrap != null ? wrap : myDefaultWrap; + } + + public Config setNoWrap(IElementType elementType) { + myWraps.put(elementType, NO_WRAP); + return this; + } + + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodProcessor.java b/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodProcessor.java index f387d859f4c3..bea335dbc17c 100644 --- a/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodProcessor.java @@ -333,8 +333,8 @@ public class ConvertToInstanceMethodProcessor extends BaseRefactoringProcessor { final PsiReference reference = usage.getReferenceExpression(); if (reference instanceof PsiReferenceExpression) { final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)reference; - if (referenceExpression.getParent() instanceof PsiReferenceExpression) { - // todo: check for correctness + PsiElement parent = referenceExpression.getParent(); + if (parent instanceof PsiReferenceExpression && sameUnqualified(parent)) { referenceExpression.delete(); } else { @@ -350,6 +350,16 @@ public class ConvertToInstanceMethodProcessor extends BaseRefactoringProcessor { } } + private static boolean sameUnqualified(PsiElement parent) { + PsiElement resolve = ((PsiReferenceExpression)parent).resolve(); + if (resolve instanceof PsiField) { + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(resolve.getProject()); + final PsiExpression unqualifiedFieldReference = elementFactory.createExpressionFromText(((PsiField)resolve).getName(), parent); + return resolve == ((PsiReferenceExpression)unqualifiedFieldReference).resolve(); + } + return true; + } + private void processMethodCall(MethodCallUsageInfo usageInfo) throws IncorrectOperationException { PsiMethodCallExpression methodCall = usageInfo.getMethodCall(); PsiParameterList parameterList = myMethod.getParameterList(); diff --git a/java/java-impl/src/com/intellij/refactoring/invertBoolean/JavaInvertBooleanDelegate.java b/java/java-impl/src/com/intellij/refactoring/invertBoolean/JavaInvertBooleanDelegate.java index 281629dbe9c9..8b09aec1f867 100644 --- a/java/java-impl/src/com/intellij/refactoring/invertBoolean/JavaInvertBooleanDelegate.java +++ b/java/java-impl/src/com/intellij/refactoring/invertBoolean/JavaInvertBooleanDelegate.java @@ -108,13 +108,6 @@ public class JavaInvertBooleanDelegate extends InvertBooleanDelegate { elementsToInvert.add(refElement); } } - - if (namedElement instanceof PsiVariable) { - final PsiExpression initializer = ((PsiVariable)namedElement).getInitializer(); - if (initializer != null) { - elementsToInvert.add(initializer); - } - } } public PsiElement getElementToInvert(PsiElement namedElement, PsiElement element) { @@ -172,16 +165,21 @@ public class JavaInvertBooleanDelegate extends InvertBooleanDelegate { } @Override - public void invertDefaultElementInitializer(final PsiElement element) { + public void invertElementInitializer(final PsiElement element) { if (element instanceof PsiField && ((PsiField)element).getInitializer() == null) { ((PsiField)element).setInitializer(JavaPsiFacade.getElementFactory(element.getProject()).createExpressionFromText("true", element)); + } else if (element instanceof PsiVariable) { + final PsiExpression initializer = ((PsiVariable)element).getInitializer(); + if (initializer != null) { + replaceWithNegatedExpression(initializer); + } } } public void collectRefElements(final PsiElement element, - final Collection elementsToInvert, final RenameProcessor renameProcessor, - @NotNull final String newName) { + @NotNull final String newName, + final Collection elementsToInvert) { collectRefsToInvert(element, elementsToInvert); if (element instanceof PsiMethod) { @@ -255,7 +253,7 @@ public class JavaInvertBooleanDelegate extends InvertBooleanDelegate { } @Override - public void findConflicts(MultiMap conflicts, UsageInfo[] usageInfos) { + public void findConflicts(UsageInfo[] usageInfos, MultiMap conflicts) { for (UsageInfo info : usageInfos) { final PsiElement element = info.getElement(); if (element instanceof PsiMethodReferenceExpression) { diff --git a/java/java-impl/src/com/intellij/testIntegration/TestRunLineMarkerProvider.java b/java/java-impl/src/com/intellij/testIntegration/TestRunLineMarkerProvider.java index 1a400d50282a..17c8ab8af80e 100644 --- a/java/java-impl/src/com/intellij/testIntegration/TestRunLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/testIntegration/TestRunLineMarkerProvider.java @@ -16,16 +16,23 @@ package com.intellij.testIntegration; import com.intellij.codeInsight.TestFrameworks; +import com.intellij.execution.TestStateStorage; import com.intellij.execution.lineMarker.ExecutorAction; import com.intellij.execution.lineMarker.RunLineMarkerContributor; +import com.intellij.execution.testframework.TestIconMapper; +import com.intellij.execution.testframework.sm.runner.states.TestStateInfo; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiIdentifier; import com.intellij.psi.PsiMethod; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Function; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; + /** * @author Dmitry Avdeev */ @@ -46,7 +53,8 @@ public class TestRunLineMarkerProvider extends RunLineMarkerContributor { if (element instanceof PsiClass) { TestFramework framework = TestFrameworks.detectFramework((PsiClass)element); if (framework != null && framework.isTestClass(element)) { - return new Info(framework.getIcon(), TOOLTIP_PROVIDER, ExecutorAction.getActions(1)); + String url = "java:suite://" + ((PsiClass)element).getQualifiedName(); + return getInfo(url, framework, e.getProject()); } } if (element instanceof PsiMethod) { @@ -54,8 +62,8 @@ public class TestRunLineMarkerProvider extends RunLineMarkerContributor { if (psiClass != null) { TestFramework framework = TestFrameworks.detectFramework(psiClass); if (framework != null && framework.isTestMethod(element)) { - // String url = "java:test://" + psiClass.getQualifiedName() + "." + ((PsiMethod)element).getName(); - return new Info(framework.getIcon(), TOOLTIP_PROVIDER, ExecutorAction.getActions(1)); + String url = "java:test://" + psiClass.getQualifiedName() + "." + ((PsiMethod)element).getName(); + return getInfo(url, framework, e.getProject()); } } } @@ -63,7 +71,20 @@ public class TestRunLineMarkerProvider extends RunLineMarkerContributor { return null; } + @NotNull + protected Info getInfo(String url, TestFramework framework, Project project) { + Icon icon = getTestStateIcon(url, project); + return new Info(icon == null ? framework.getIcon() : icon, TOOLTIP_PROVIDER, ExecutorAction.getActions(1)); + } + protected boolean isIdentifier(PsiElement e) { return e instanceof PsiIdentifier; } + + private static Icon getTestStateIcon(String url, Project project) { + TestStateStorage.Record state = TestStateStorage.getInstance(project).getState(url); + if (state == null) return null; + TestStateInfo.Magnitude magnitude = TestIconMapper.getMagnitude(state.magnitude); + return TestIconMapper.getIcon(magnitude); + } } diff --git a/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java b/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java index 1f8284924151..2b566b18b45f 100644 --- a/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java +++ b/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java @@ -1301,29 +1301,39 @@ class ControlFlowAnalyzer extends JavaElementVisitor { if (rExpr != null) { rExpr.accept(this); } - final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lExpr; - PsiExpression qualifierExpression = referenceExpression.getQualifierExpression(); - PsiVariable variable = getUsedVariable(referenceExpression); - if (qualifierExpression == null || - qualifierExpression instanceof PsiThisExpression || - variable instanceof PsiField && variable.hasModifierProperty(PsiModifier.STATIC)) { - if (variable != null) { - if (myAssignmentTargetsAreElements) { - startElement(lExpr); - } - - if (expression.getOperationTokenType() != JavaTokenType.EQ) { - generateReadInstruction(variable); - } - generateWriteInstruction(variable); - - if (myAssignmentTargetsAreElements) finishElement(lExpr); + PsiVariable variable = getUsedVariable((PsiReferenceExpression)lExpr); + if (variable != null) { + if (myAssignmentTargetsAreElements) { + startElement(lExpr); } + + if (expression.getOperationTokenType() != JavaTokenType.EQ) { + generateReadInstruction(variable); + } + generateWriteInstruction(variable); + + if (myAssignmentTargetsAreElements) finishElement(lExpr); } else { lExpr.accept(this); //? } } + else if (lExpr instanceof PsiArrayAccessExpression && + ((PsiArrayAccessExpression)lExpr).getArrayExpression() instanceof PsiReferenceExpression){ + PsiVariable variable = getUsedVariable((PsiReferenceExpression)((PsiArrayAccessExpression)lExpr).getArrayExpression()); + if (variable != null) { + generateReadInstruction(variable); + final PsiExpression indexExpression = ((PsiArrayAccessExpression)lExpr).getIndexExpression(); + if (indexExpression != null) { + indexExpression.accept(this); + } + } else { + lExpr.accept(this); + } + if (rExpr != null) { + rExpr.accept(this); + } + } else if (lExpr != null) { lExpr.accept(this); if (rExpr != null) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/DelegateSubstitutor.java b/java/java-psi-impl/src/com/intellij/psi/impl/DelegateSubstitutor.java deleted file mode 100644 index fa8b1bef1a24..000000000000 --- a/java/java-psi-impl/src/com/intellij/psi/impl/DelegateSubstitutor.java +++ /dev/null @@ -1,84 +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.psi.impl; - -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiSubstitutor; -import com.intellij.psi.PsiType; -import com.intellij.psi.PsiTypeParameter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; - -public class DelegateSubstitutor implements PsiSubstitutor { - - private final @NotNull PsiSubstitutor myDelegate; - - public DelegateSubstitutor(@NotNull PsiSubstitutor delegate) { - myDelegate = delegate; - } - - @Override - @Nullable - public PsiType substitute(@NotNull PsiTypeParameter typeParameter) { - return myDelegate.substitute(typeParameter); - } - - @Override - public PsiType substitute(@Nullable PsiType type) { - return myDelegate.substitute(type); - } - - @Override - public PsiType substituteWithBoundsPromotion(@NotNull PsiTypeParameter typeParameter) { - return myDelegate.substituteWithBoundsPromotion(typeParameter); - } - - @Override - @NotNull - public PsiSubstitutor put(@NotNull PsiTypeParameter classParameter, PsiType mapping) { - return myDelegate.put(classParameter, mapping); - } - - @Override - @NotNull - public PsiSubstitutor putAll(@NotNull PsiClass parentClass, PsiType[] mappings) { - return myDelegate.putAll(parentClass, mappings); - } - - @Override - @NotNull - public PsiSubstitutor putAll(@NotNull PsiSubstitutor another) { - return myDelegate.putAll(another); - } - - @Override - @NotNull - public Map getSubstitutionMap() { - return myDelegate.getSubstitutionMap(); - } - - @Override - public boolean isValid() { - return myDelegate.isValid(); - } - - @Override - public void ensureValid() { - myDelegate.ensureValid(); - } -} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSuperMethodImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSuperMethodImplUtil.java index b670a0c02ef1..f61482ed719a 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSuperMethodImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSuperMethodImplUtil.java @@ -313,9 +313,8 @@ public class PsiSuperMethodImplUtil { return false; } - if (superMethod.hasModifierProperty(PsiModifier.DEFAULT) || - method.hasModifierProperty(PsiModifier.DEFAULT)) { - return !InheritanceUtil.isInheritorOrSelf(superClass, containingClass, true); + if (superMethod.hasModifierProperty(PsiModifier.DEFAULT) || method.hasModifierProperty(PsiModifier.DEFAULT)) { + return superMethod.equals(method) || !InheritanceUtil.isInheritorOrSelf(superClass, containingClass, true); } return true; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index b0ed00a69e25..fbfe8c1160d6 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -1290,10 +1290,9 @@ public class InferenceSession { // the type to search is the result of capture conversion (5.1.10) applied to T; // otherwise, the type to search is the same as the type of the first search. Again, the type arguments, if any, are given by the method reference. if (PsiUtil.isRawSubstitutor(containingClass, psiSubstitutor)) { - final PsiClassType.ClassResolveResult pResult = PsiUtil.resolveGenericsClassInType(PsiImplUtil.normalizeWildcardTypeByPosition(pType, (PsiExpression)myContext)); - final PsiClass pClass = pResult.getElement(); - final PsiSubstitutor receiverSubstitutor = pClass != null ? TypeConversionUtil - .getClassSubstitutor(containingClass, pClass, pResult.getSubstitutor()) : null; + PsiType normalizedPType = PsiImplUtil.normalizeWildcardTypeByPosition(pType, (PsiExpression)myContext); + final PsiSubstitutor receiverSubstitutor = PsiMethodReferenceCompatibilityConstraint + .getParameterizedTypeSubstitutor(containingClass, normalizedPType); if (receiverSubstitutor != null) { if (!method.hasTypeParameters()) { if (signature.getParameterTypes().length == 1 || PsiUtil.isRawSubstitutor(containingClass, receiverSubstitutor)) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java index 4609b7844a7c..925582ba4921 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java @@ -22,10 +22,7 @@ import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfacePa import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; import com.intellij.psi.infos.MethodCandidateInfo; -import com.intellij.psi.util.MethodSignature; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.psi.util.*; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Nullable; @@ -219,13 +216,8 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm // otherwise, the type to search is the same as the type of the first search. Again, the type arguments, if any, are given by the method reference. if ( PsiUtil.isRawSubstitutor(qContainingClass, psiSubstitutor)) { if (member instanceof PsiMethod && PsiMethodReferenceUtil.isSecondSearchPossible(signature.getParameterTypes(), qualifierResolveResult, myExpression)) { - final PsiType pType = signature.getParameterTypes()[0]; - PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType( - PsiImplUtil.normalizeWildcardTypeByPosition(pType, myExpression)); - PsiClass paramClass = resolveResult.getElement(); - LOG.assertTrue(paramClass != null); - psiSubstitutor = TypeConversionUtil.getClassSubstitutor(qContainingClass, paramClass, resolveResult.getSubstitutor()); - LOG.assertTrue(psiSubstitutor != null); + final PsiType pType = PsiImplUtil.normalizeWildcardTypeByPosition(signature.getParameterTypes()[0], myExpression); + psiSubstitutor = getParameterizedTypeSubstitutor(qContainingClass, pType); } else if (member instanceof PsiMethod && ((PsiMethod)member).isConstructor() || member instanceof PsiClass) { //15.13.1 @@ -253,6 +245,24 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm return psiSubstitutor; } + public static PsiSubstitutor getParameterizedTypeSubstitutor(PsiClass qContainingClass, PsiType pType) { + if (pType instanceof PsiIntersectionType) { + for (PsiType type : ((PsiIntersectionType)pType).getConjuncts()) { + PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(type); + if (InheritanceUtil.isInheritorOrSelf(resolveResult.getElement(), qContainingClass, true)) { + return getParameterizedTypeSubstitutor(qContainingClass, type); + } + } + } + + PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(pType); + PsiClass paramClass = resolveResult.getElement(); + LOG.assertTrue(paramClass != null); + PsiSubstitutor psiSubstitutor = TypeConversionUtil.getClassSubstitutor(qContainingClass, paramClass, resolveResult.getSubstitutor()); + LOG.assertTrue(psiSubstitutor != null); + return psiSubstitutor; + } + @Override public void apply(PsiSubstitutor substitutor, boolean cache) { myT = substitutor.substitute(myT); diff --git a/java/java-tests/testData/codeInsight/completion/keywords/extendsAfterClassGenerics.java b/java/java-tests/testData/codeInsight/completion/keywords/extendsAfterClassGenerics.java new file mode 100644 index 000000000000..75c68e89af14 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/keywords/extendsAfterClassGenerics.java @@ -0,0 +1,2 @@ +class A { +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/AnnotationBeforeIdentifier.java b/java/java-tests/testData/codeInsight/completion/normal/AnnotationBeforeIdentifier.java new file mode 100644 index 000000000000..b22b28430c1d --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AnnotationBeforeIdentifier.java @@ -0,0 +1,5 @@ +@interface AbcdAnno {} + +class Foo { + void foo(@AbcFoo f) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/AnnotationBeforeIdentifier_after.java b/java/java-tests/testData/codeInsight/completion/normal/AnnotationBeforeIdentifier_after.java new file mode 100644 index 000000000000..f289f7584e99 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AnnotationBeforeIdentifier_after.java @@ -0,0 +1,5 @@ +@interface AbcdAnno {} + +class Foo { + void foo(@AbcdAnno Foo f) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/SmartEnterInsideArrayBrackets.java b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterInsideArrayBrackets.java new file mode 100644 index 000000000000..1c7f2fa998d9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterInsideArrayBrackets.java @@ -0,0 +1,8 @@ +class Main { + public void test() { + double[] d = new double[si]; + } + + int size(); + int size2(); +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/SmartEnterInsideArrayBrackets_after.java b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterInsideArrayBrackets_after.java new file mode 100644 index 000000000000..68f51a6c24dc --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterInsideArrayBrackets_after.java @@ -0,0 +1,8 @@ +class Main { + public void test() { + double[] d = new double[size()]; + } + + int size(); + int size2(); +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/UnreachableAssignments.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/UnreachableAssignments.java new file mode 100644 index 000000000000..bd7ebff077a8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/UnreachableAssignments.java @@ -0,0 +1,31 @@ +class Test { + String[] days = {"",""}; + + public static void foo1() { + for (; ; ) { + break; + new Test().days = null; + } + } + + void bar1() { + for (; ; ) { + break; + days = null; + } + } + + static void foo2() { + for (; ; ) { + break; + new Test().days[0] = null; + } + } + + void bar2() { + for (;;) { + break; + days[0] = null; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/interfaceMethods/InheritUnrelatedDefaults.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/interfaceMethods/InheritUnrelatedDefaults.java index 1954feb0ef22..dc3e1e3950cb 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/interfaceMethods/InheritUnrelatedDefaults.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/interfaceMethods/InheritUnrelatedDefaults.java @@ -12,3 +12,12 @@ interface F { } interface G extends E, F {} + +interface H { + default void m() {} +} +interface K { + default void m() {} +} +class L implements H {} +class M extends L implements H, K {} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/IntersectionTypesInReceiverPosition.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/IntersectionTypesInReceiverPosition.java new file mode 100644 index 000000000000..fd4d327d3ba3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/IntersectionTypesInReceiverPosition.java @@ -0,0 +1,23 @@ +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +abstract class Constraint> { + + protected abstract Stream evStream(); + + private void foo(final Set ctrlSTCs, + final Set probCstrs) { + ArrayList a = new ArrayList(Stream + .concat(ctrlSTCs.stream(), probCstrs.stream()) + .flatMap(Constraint::evStream) + .collect(Collectors.toSet())); + } + + private abstract class CConstraint extends Constraint implements I {} + private abstract class BConstraint extends Constraint implements I {} + + interface I {} + interface Event {} + +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/convertSwitchToIf/after4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/convertSwitchToIf/after4.java new file mode 100644 index 000000000000..8ab4527ff5a4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/convertSwitchToIf/after4.java @@ -0,0 +1,13 @@ +// "Replace 'switch' with 'if'" "true" +class Precedence { + + void m() { + int a = 10; + if ((a & 1) == 0) { + System.out.println("0"); + + } else if ((a & 1) == 1) { + System.out.println("1"); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/convertSwitchToIf/before4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/convertSwitchToIf/before4.java new file mode 100644 index 000000000000..7301511c7ef4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/convertSwitchToIf/before4.java @@ -0,0 +1,14 @@ +// "Replace 'switch' with 'if'" "true" +class Precedence { + + void m() { + int a = 10; + switch(a & 1) { + case 0: + System.out.println("0"); + break; + case 1: + System.out.println("1"); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/gotosuper/DeceivingSiblingInheritance.java b/java/java-tests/testData/codeInsight/gotosuper/DeceivingSiblingInheritance.java new file mode 100644 index 000000000000..ba0319a80e0c --- /dev/null +++ b/java/java-tests/testData/codeInsight/gotosuper/DeceivingSiblingInheritance.java @@ -0,0 +1,17 @@ +package z; + +interface FileType { + String getName(); +} +abstract class LanguageFileType implements FileType { + +} +abstract class OCBaseLanguageFileType extends LanguageFileType { + public String getName() { + return ""; + } +} + +public class XibFileType extends OCBaseLanguageFileType implements FileType { + +} \ No newline at end of file diff --git a/java/java-tests/testData/find/findInEditor/BasicFind.gold b/java/java-tests/testData/find/findInEditor/BasicFind.gold index bac33ec850e2..2b40bfc71aa4 100644 --- a/java/java-tests/testData/find/findInEditor/BasicFind.gold +++ b/java/java-tests/testData/find/findInEditor/BasicFind.gold @@ -27,6 +27,6 @@ searchInProjectFiles =false [[]]b -- -RangeHighlighter: (0,1); layer:5999; tooltip: null : [null,java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] +RangeHighlighter: (0,1); layer:5999; tooltip: null : [java.awt.Color[r=0,g=0,b=0],java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] RangeHighlighter: (0,1); layer:5999; tooltip: null : [null,null,0,ROUNDED_BOX,java.awt.Color[r=0,g=0,b=0],null] ------------ diff --git a/java/java-tests/testData/find/findInEditor/EmacsLikeFallback.gold b/java/java-tests/testData/find/findInEditor/EmacsLikeFallback.gold index 3271a31c16db..49e83094c8f5 100644 --- a/java/java-tests/testData/find/findInEditor/EmacsLikeFallback.gold +++ b/java/java-tests/testData/find/findInEditor/EmacsLikeFallback.gold @@ -28,9 +28,9 @@ searchInProjectFiles =false [[]] [a]b -- -RangeHighlighter: (0,1); layer:5999; tooltip: null : [null,java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] +RangeHighlighter: (0,1); layer:5999; tooltip: null : [java.awt.Color[r=0,g=0,b=0],java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] RangeHighlighter: (0,1); layer:5999; tooltip: null : [null,null,0,ROUNDED_BOX,java.awt.Color[r=0,g=0,b=0],null] -RangeHighlighter: (2,3); layer:5999; tooltip: null : [null,java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] +RangeHighlighter: (2,3); layer:5999; tooltip: null : [java.awt.Color[r=0,g=0,b=0],java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] ------------ --- FIND MODEL --- myStringToFind =ab @@ -62,7 +62,7 @@ searchInProjectFiles =false a [[]] -- -RangeHighlighter: (2,4); layer:5999; tooltip: null : [null,java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] +RangeHighlighter: (2,4); layer:5999; tooltip: null : [java.awt.Color[r=0,g=0,b=0],java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] RangeHighlighter: (2,4); layer:5999; tooltip: null : [null,null,0,ROUNDED_BOX,java.awt.Color[r=0,g=0,b=0],null] ------------ --- FIND MODEL --- @@ -95,7 +95,7 @@ searchInProjectFiles =false [[]] [a]b -- -RangeHighlighter: (0,1); layer:5999; tooltip: null : [null,java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] +RangeHighlighter: (0,1); layer:5999; tooltip: null : [java.awt.Color[r=0,g=0,b=0],java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] RangeHighlighter: (0,1); layer:5999; tooltip: null : [null,null,0,ROUNDED_BOX,java.awt.Color[r=0,g=0,b=0],null] -RangeHighlighter: (2,3); layer:5999; tooltip: null : [null,java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] +RangeHighlighter: (2,3); layer:5999; tooltip: null : [java.awt.Color[r=0,g=0,b=0],java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] ------------ diff --git a/java/java-tests/testData/find/findInEditor/ReplacementWithEmptyString.gold b/java/java-tests/testData/find/findInEditor/ReplacementWithEmptyString.gold index f1141673aa65..0f3f05980865 100644 --- a/java/java-tests/testData/find/findInEditor/ReplacementWithEmptyString.gold +++ b/java/java-tests/testData/find/findInEditor/ReplacementWithEmptyString.gold @@ -57,7 +57,7 @@ searchInProjectFiles =false [[]] -- -RangeHighlighter: (0,1); layer:5999; tooltip: null : [null,java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] +RangeHighlighter: (0,1); layer:5999; tooltip: null : [java.awt.Color[r=0,g=0,b=0],java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] RangeHighlighter: (0,1); layer:5999; tooltip: null : [null,null,0,ROUNDED_BOX,java.awt.Color[r=0,g=0,b=0],null] ------------ --- FIND MODEL --- @@ -91,7 +91,7 @@ Replacement Preview: [[]] -- -RangeHighlighter: (0,1); layer:5999; tooltip: null : [null,java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] +RangeHighlighter: (0,1); layer:5999; tooltip: null : [java.awt.Color[r=0,g=0,b=0],java.awt.Color[r=255,g=255,b=0],0,BOXED,null,java.awt.Color[r=113,g=179,b=98]] RangeHighlighter: (0,1); layer:5999; tooltip: null : [null,null,0,ROUNDED_BOX,java.awt.Color[r=0,g=0,b=0],null] ------------ --- FIND MODEL --- diff --git a/java/java-tests/testData/refactoring/convertToInstanceMethod/ConflictingParameterName.java b/java/java-tests/testData/refactoring/convertToInstanceMethod/ConflictingParameterName.java new file mode 100644 index 000000000000..08b546c1c8ab --- /dev/null +++ b/java/java-tests/testData/refactoring/convertToInstanceMethod/ConflictingParameterName.java @@ -0,0 +1,15 @@ +class Foo { + + public String bar; + private String baz; + + public static void setBar(Foo foo, String bar) { + foo.bar = bar; + foo.baz = bar; + foo.bar(); + } + + private void bar() { + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/convertToInstanceMethod/ConflictingParameterName.java.after b/java/java-tests/testData/refactoring/convertToInstanceMethod/ConflictingParameterName.java.after new file mode 100644 index 000000000000..6ff17b77e378 --- /dev/null +++ b/java/java-tests/testData/refactoring/convertToInstanceMethod/ConflictingParameterName.java.after @@ -0,0 +1,15 @@ +class Foo { + + public String bar; + private String baz; + + public void setBar(String bar) { + this.bar = bar; + baz = bar; + bar(); + } + + private void bar() { + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineLocal/ArrayMethodCallInitialized.java b/java/java-tests/testData/refactoring/inlineLocal/ArrayMethodCallInitialized.java index aa7c17cf6fb1..c86745b47f9a 100644 --- a/java/java-tests/testData/refactoring/inlineLocal/ArrayMethodCallInitialized.java +++ b/java/java-tests/testData/refactoring/inlineLocal/ArrayMethodCallInitialized.java @@ -1,8 +1,8 @@ public class A { public void testInlineRefactoring() { - int[] array = ar(); - array[1] = 22; + int[] array = ar(); + array[1] = 22; } private int[] ar() { diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavadocCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavadocCompletionTest.groovy index 0c7f62a26ea5..317a3dd61cc9 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavadocCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavadocCompletionTest.groovy @@ -545,4 +545,13 @@ class Test { registrar.unregisterReferenceProvider(PsiDocTag.class, provider); } } + + public void "test complete author name"() { + def userName = SystemProperties.userName + assert userName + myFixture.configureByText 'a.java', "/** @author */" + myFixture.completeBasic() + myFixture.type('\n') + myFixture.checkResult "/** @author $userName */" + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/KeywordCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/KeywordCompletionTest.java index b2d2d76ceeb5..ec6cf94b62ac 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/KeywordCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/KeywordCompletionTest.java @@ -64,6 +64,7 @@ public class KeywordCompletionTest extends LightCompletionTestCase { public void testExtends11() throws Exception { doTest(false); } public void testExtends12() throws Exception { doTest(false); } public void testExtends13() throws Exception { doTest(false); } + public void testExtendsAfterClassGenerics() throws Exception { doTest(2, "extends", "implements"); } public void testSynchronized1() throws Exception { doTest(false); } public void testSynchronized2() throws Exception { diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index 1e79ca0a017a..43601be439fd 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -935,6 +935,7 @@ public class ListUtils { public void testSmartEnterNoNewLine() { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) } public void testSmartEnterWithNewLine() { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) } public void testSmartEnterGuessArgumentCount() throws Throwable { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) } + public void testSmartEnterInsideArrayBrackets() { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) } public void testTabReplacesMethodNameWithLocalVariableName() throws Throwable { doTest('\t'); } public void testMethodParameterAnnotationClass() throws Throwable { doTest(); } @@ -1004,6 +1005,7 @@ public class ListUtils { public void testOnlyAnnotationsAfterAt() throws Throwable { doTest() } public void testOnlyAnnotationsAfterAt2() throws Throwable { doTest('\n') } + public void testAnnotationBeforeIdentifier() { doTest('\n') } public void testOnlyExceptionsInCatch1() throws Exception { doTest('\n') } public void testOnlyExceptionsInCatch2() throws Exception { doTest('\n') } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SecondSmartTypeCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SecondSmartTypeCompletionTest.java index 5708b69d4bfb..8ee7fb6cb635 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SecondSmartTypeCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SecondSmartTypeCompletionTest.java @@ -1,9 +1,7 @@ package com.intellij.codeInsight.completion; import com.intellij.JavaTestUtil; -import com.intellij.codeInsight.lookup.LookupElementDecorator; import com.intellij.codeInsight.lookup.LookupElementPresentation; -import com.intellij.codeInsight.lookup.LookupItem; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.testFramework.PlatformTestUtil; import com.intellij.util.ThrowableRunnable; @@ -115,8 +113,8 @@ public class SecondSmartTypeCompletionTest extends LightFixtureCompletionTestCas configure(); checkResultByFile(getTestName(false) + ".java"); assertStringItems("bar()", "foo()"); - assertEquals("Arrays.asList(f.bar())", ((LookupItem)((LookupElementDecorator)myItems[0]).getDelegate()).getPresentableText()); - assertEquals("Arrays.asList(f.foo())", ((LookupItem)((LookupElementDecorator)myItems[1]).getDelegate()).getPresentableText()); + assertEquals("Arrays.asList(f.bar())", LookupElementPresentation.renderElement(myItems[0]).getItemText()); + assertEquals("Arrays.asList(f.foo())", LookupElementPresentation.renderElement(myItems[1]).getItemText()); selectItem(myItems[1]); checkResult(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk6Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk6Test.java index b699cf9167ec..17c0e5d13d93 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk6Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk6Test.java @@ -58,4 +58,5 @@ public class LightAdvHighlightingJdk6Test extends LightDaemonAnalyzerTestCase { public void testGenericArrayCreationWithGenericTypeWithOneUnboundedWildcardOneNormalParams() { doTest(false, false); } public void testAgentPremain() { doTest(false, false); } public void testInitializedBeforeUsed() { doTest(false, false); } + public void testUnreachableAssignments() { doTest(false, false); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/JavaGotoSuperTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/JavaGotoSuperTest.java index b69777ae7a25..5fc8d7b07da2 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/JavaGotoSuperTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/JavaGotoSuperTest.java @@ -117,4 +117,25 @@ public class JavaGotoSuperTest extends LightDaemonAnalyzerTestCase { checkResultByFile(getBasePath() + "SiblingInheritance.java"); } + public void testDoNotShowSiblingInheritanceLineMarkerIfSubclassImplementsTheSameInterfaceAsTheCurrentClass() throws Throwable { + configureByFile(getBasePath() + "DeceivingSiblingInheritance.java"); + PsiJavaFile file = (PsiJavaFile)getFile(); + PsiClass OCBaseLanguageFileType = JavaPsiFacade.getInstance(getProject()).findClass("z.OCBaseLanguageFileType", GlobalSearchScope.fileScope(file)); + PsiMethod getName = OCBaseLanguageFileType.getMethods()[0]; + assertEquals("getName", getName.getName()); + + doHighlighting(); + Document document = getEditor().getDocument(); + List markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject()); + List inMyClass = ContainerUtil.filter(markers, info -> { + return OCBaseLanguageFileType.getTextRange().containsRange(info.startOffset, info.endOffset); + }); + assertTrue(inMyClass.toString(), inMyClass.size() == 2); + LineMarkerInfo iMarker = findMarkerWithElement(inMyClass, getName.getNameIdentifier()); + assertSame(MarkerType.OVERRIDING_METHOD.getNavigationHandler(), iMarker.getNavigationHandler()); + + LineMarkerInfo aMarker = findMarkerWithElement(inMyClass, OCBaseLanguageFileType.getNameIdentifier()); + assertSame(MarkerType.SUBCLASSED_CLASS.getNavigationHandler(), aMarker.getNavigationHandler()); + } + } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java index 9b4c44547ed4..97683fca3d57 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java @@ -106,6 +106,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testIntersectionTypesInReceiverPosition() throws Exception { + doTest(); + } + private void doTest() { doTest(false); } diff --git a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java index 16320628b403..d4f57bb69690 100644 --- a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java +++ b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java @@ -194,10 +194,9 @@ public class FindManagerTest extends DaemonAnalyzerTestCase { } private List findUsages(@NotNull FindModel findModel) { - PsiDirectory psiDirectory = FindInProjectUtil.getPsiDirectory(findModel, myProject); List result = new ArrayList<>(); final CommonProcessors.CollectProcessor collector = new CommonProcessors.CollectProcessor<>(result); - FindInProjectUtil.findUsages(findModel, psiDirectory, myProject, collector, new FindUsagesProcessPresentation(FindInProjectUtil.setupViewPresentation(true, findModel))); + FindInProjectUtil.findUsages(findModel, myProject, collector, new FindUsagesProcessPresentation(FindInProjectUtil.setupViewPresentation(true, findModel))); return result; } @@ -661,6 +660,20 @@ public class FindManagerTest extends DaemonAnalyzerTestCase { } } + public void testFindInExcludedDirectory() throws Exception { + VirtualFile root = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(createTempDirectory()); + addSourceContentToRoots(myModule, root); + VirtualFile excluded = createChildDirectory(root, "excluded"); + createFile(myModule, excluded, "a.txt", "foo bar foo"); + PsiTestUtil.addExcludedRoot(myModule, excluded); + + FindModel findModel = FindManagerTestUtils.configureFindModel("foo"); + findModel.setWholeWordsOnly(true); + findModel.setProjectScope(false); + findModel.setDirectoryName(excluded.getPath()); + assertSize(2, findUsages(findModel)); + } + public void testFindInJavaDocs() { FindModel findModel = FindManagerTestUtils.configureFindModel("done"); String text = "/** done done done */"; diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaEnterActionTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaEnterActionTest.java new file mode 100644 index 000000000000..fd6ba563d2c0 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaEnterActionTest.java @@ -0,0 +1,104 @@ +/* + * 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.formatter.java; + +import com.intellij.codeInsight.AbstractEnterActionTestCase; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.JavaCodeStyleSettings; + +import java.io.IOException; + +public class JavaEnterActionTest extends AbstractEnterActionTestCase { + + public void testEnterInsideAnnotationParameters() throws IOException { + CodeStyleSettings settings = getCodeStyleSettings(); + JavaCodeStyleSettings javaSettings = settings.getCustomSettings(JavaCodeStyleSettings.class); + javaSettings.ALIGN_MULTILINE_ANNOTATION_PARAMETERS = true; + setCodeStyleSettings(settings); + + doTextTest("java", + "public class T {\n" + + "\n" + + " @Configurable(order = 25, \n" + + " validator = BigDecimalPercentValidator.class, )\n" + + " public void run() {\n" + + " }\n" + + " \n" + + " \n" + + "}", + "public class T {\n" + + "\n" + + " @Configurable(order = 25, \n" + + " validator = BigDecimalPercentValidator.class, \n" + + " )\n" + + " public void run() {\n" + + " }\n" + + " \n" + + " \n" + + "}"); + + doTextTest("java", + "public class T {\n" + + "\n" + + " @Configurable(order = 25, \n" + + " validator = BigDecimalPercentValidator.class, \n" + + " )\n" + + " public void run() {\n" + + " }\n" + + " \n" + + " \n" + + "}", + "public class T {\n" + + "\n" + + " @Configurable(order = 25, \n" + + " validator = BigDecimalPercentValidator.class, \n" + + " \n" + + " )\n" + + " public void run() {\n" + + " }\n" + + " \n" + + " \n" + + "}"); + } + + public void testEnterInsideAnnotationParameters_AfterNameValuePairBeforeLparenth() throws IOException { + CodeStyleSettings settings = getCodeStyleSettings(); + JavaCodeStyleSettings javaSettings = settings.getCustomSettings(JavaCodeStyleSettings.class); + javaSettings.ALIGN_MULTILINE_ANNOTATION_PARAMETERS = true; + setCodeStyleSettings(settings); + + doTextTest("java", + "public class T {\n" + + "\n" + + " @Configurable(order = 25, \n" + + " validator = BigDecimalPercentValidator.class)\n" + + " public void run() {\n" + + " }\n" + + " \n" + + " \n" + + "}", + "public class T {\n" + + "\n" + + " @Configurable(order = 25, \n" + + " validator = BigDecimalPercentValidator.class\n" + + " )\n" + + " public void run() {\n" + + " }\n" + + " \n" + + " \n" + + "}"); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java index 128a46dd72ac..4e5de09c5679 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java @@ -3204,4 +3204,19 @@ public void testSCR260() throws Exception { "}" ); } + + public void testReformatCodeWithErrorElementsWithoutAssertions() { + doTextTest("class RedTest { \n\n\n\n\n\n\n\n " + + "String [ ] [ ] test = { { \n\n\n\n\n { \"\"} \n\n\n\n\n }; " + + "String [ ] [ ] test = { { \n\n\n\n\n { \"\"} \n\n\n\n\n }; " + + " \n\n\n\n\n\n\n\n } ", + "class RedTest {\n\n\n" + + " String[][] test = {{\n\n\n" + + " {\"\"}\n\n\n" + + " };\n" + + " String[][] test = {{\n\n\n" + + " {\"\"}\n\n\n" + + " };\n\n\n" + + "} "); + } } diff --git a/java/java-tests/testSrc/com/intellij/psi/search/SearchInLibsTest.java b/java/java-tests/testSrc/com/intellij/psi/search/SearchInLibsTest.java index fce6d92714f8..445267afcf7b 100644 --- a/java/java-tests/testSrc/com/intellij/psi/search/SearchInLibsTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/search/SearchInLibsTest.java @@ -86,7 +86,7 @@ public class SearchInLibsTest extends PsiTestCase { model.setProjectScope(false); List usages = new ArrayList(); - FindInProjectUtil.findUsages(model, aClass.getContainingFile().getContainingDirectory(), getProject(), + FindInProjectUtil.findUsages(model, getProject(), new CommonProcessors.CollectProcessor( usages), FindInProjectUtil .setupProcessPresentation(getProject(), false, FindInProjectUtil.setupViewPresentation(false, model))); diff --git a/java/java-tests/testSrc/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodTest.java b/java/java-tests/testSrc/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodTest.java index 50d7b0f649ca..06aa5d47e129 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodTest.java @@ -35,6 +35,11 @@ public class ConvertToInstanceMethodTest extends LightRefactoringTestCase { public void testInterfaceTypeParameter() throws Exception { doTest(0); } public void testJavadocParameter() throws Exception { doTest(0); } + + public void testConflictingParameterName() throws Exception { + doTest(0); + } + public void testVisibilityConflict() throws Exception { try { doTest(0, PsiModifier.PRIVATE); diff --git a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java index 7c7b39800219..6650bb7b6bed 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java @@ -156,7 +156,7 @@ public class InlineLocalTest extends LightCodeInsightTestCase { } public void testArrayMethodCallInitialized() throws Exception { - doTest(true); + doTest(false); } public void testArrayIndex() throws Exception { diff --git a/java/remote-servers/impl/src/META-INF/RemoteServersJava.xml b/java/remote-servers/impl/src/META-INF/RemoteServersJava.xml index 692199e3bb2c..4b1b0343eca2 100644 --- a/java/remote-servers/impl/src/META-INF/RemoteServersJava.xml +++ b/java/remote-servers/impl/src/META-INF/RemoteServersJava.xml @@ -6,6 +6,9 @@ + + diff --git a/java/testFramework/src/com/intellij/codeInsight/AbstractEnterActionTestCase.java b/java/testFramework/src/com/intellij/codeInsight/AbstractEnterActionTestCase.java new file mode 100644 index 000000000000..6d27a7235382 --- /dev/null +++ b/java/testFramework/src/com/intellij/codeInsight/AbstractEnterActionTestCase.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2005 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.codeInsight; + +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.testFramework.LightCodeInsightTestCase; +import org.jetbrains.annotations.NonNls; + +import java.io.IOException; + +/** + * Created by IntelliJ IDEA. + * User: lesya + * Date: Sep 21, 2005 + * Time: 11:02:33 PM + * To change this template use File | Settings | File Templates. + */ +public abstract class AbstractEnterActionTestCase extends LightCodeInsightTestCase { + private static final String TEST_PATH = "/codeInsight/enterAction/"; + + protected static CodeStyleSettings getCodeStyleSettings() { + return CodeStyleSettingsManager.getSettings(getProject()).clone(); + } + protected static void setCodeStyleSettings(CodeStyleSettings settings) { + CodeStyleSettingsManager.getInstance(getProject()).setTemporarySettings(settings); + } + + @Override + protected void tearDown() throws Exception { + CodeStyleSettingsManager.getInstance(getProject()).dropTemporarySettings(); + super.tearDown(); + } + + protected void doGetIndentTest(final PsiFile file, final int lineNum, final String expected) { + final int offset = PsiDocumentManager.getInstance(getProject()).getDocument(file).getLineEndOffset(lineNum); + final String actial = CodeStyleManager.getInstance(getProject()).getLineIndent(file, offset); + assertEquals(expected, actial); + } + + protected void doTest() throws Exception { + doTest("java"); + } + + protected void doTextTest(@NonNls String ext, @NonNls String before, @NonNls String after) throws IOException { + configureFromFileText("a." + ext, before); + performAction(); + checkResultByText(null, after, false); + } + + protected void doTest(final String ext) throws Exception { + final String name = getTestName(false); + + configureByFile(TEST_PATH + name + "." + ext); + performAction(); + checkResultByFile(null, TEST_PATH + name + "_after." + ext, false); + } + + protected void performAction() { + type('\n'); + } +} diff --git a/platform/analysis-api/src/com/intellij/codeInspection/ex/InspectionToolWrapper.java b/platform/analysis-api/src/com/intellij/codeInspection/ex/InspectionToolWrapper.java index baaf542caed7..779910576f5f 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/ex/InspectionToolWrapper.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/ex/InspectionToolWrapper.java @@ -60,8 +60,17 @@ public abstract class InspectionToolWrapper other) { myEP = other.myEP; // we need to create a copy for buffering - //noinspection unchecked - myTool = other.myTool == null ? null : (T)InspectionToolsRegistrarCore.instantiateTool(other.myTool.getClass()); + if (myEP != null) { + //noinspection unchecked + myTool = (T)myEP.instantiateTool(); + } + else if (other.myTool != null) { + //noinspection unchecked + myTool = (T)InspectionToolsRegistrarCore.instantiateTool(other.myTool.getClass()); + } + else { + myTool = null; + } } public void initialize(@NotNull GlobalInspectionContext context) { @@ -90,7 +99,7 @@ public abstract class InspectionToolWrapper myRanges2markersCache = new THashMap(); - private volatile boolean myDisposed; private HighlightingSessionImpl(@NotNull PsiFile psiFile, @Nullable Editor editor, @@ -60,10 +58,6 @@ public class HighlightingSessionImpl implements HighlightingSession { myEditorColorsScheme = editorColorsScheme; myProject = psiFile.getProject(); myDocument = PsiDocumentManager.getInstance(myProject).getDocument(psiFile); - Disposer.register(progressIndicator, this); - if (progressIndicator.isCanceled()) { - Disposer.dispose(progressIndicator); //dispose both progress indicator and this session in case we managed to register after indicator dispose to avoid mem leaks - } } private static final Key> HIGHLIGHTING_SESSION = Key.create("HIGHLIGHTING_SESSION"); @@ -159,7 +153,7 @@ public class HighlightingSessionImpl implements HighlightingSession { @NotNull TextRange priorityRange, @NotNull TextRange restrictedRange, int groupId) { - myAddHighlighterInEDTQueue.offer(new Info(info, priorityRange, restrictedRange, groupId)); + myAddHighlighterInEDTQueue.offer(new Info(info, restrictedRange, groupId)); } void queueDisposeHighlighter(@Nullable RangeHighlighterEx highlighter) { @@ -167,20 +161,13 @@ public class HighlightingSessionImpl implements HighlightingSession { myDisposeHighlighterInEDTQueue.offer(highlighter); } - @Override - public void dispose() { - myDisposed = true; - } - private static class Info { @NotNull private final HighlightInfo myInfo; - @NotNull private final TextRange myPriorityRange; @NotNull private final TextRange myRestrictRange; private final int myGroupId; - public Info(@NotNull HighlightInfo info, @NotNull TextRange priorityRange, @NotNull TextRange restrictRange, int groupId) { + private Info(@NotNull HighlightInfo info, @NotNull TextRange restrictRange, int groupId) { myInfo = info; - myPriorityRange = priorityRange; myRestrictRange = restrictRange; myGroupId = groupId; } diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/SeverityRegistrar.java b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/SeverityRegistrar.java index da6c999f8af6..75caebfab82d 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/SeverityRegistrar.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/SeverityRegistrar.java @@ -21,10 +21,8 @@ import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.util.JDOMExternalizableStringList; -import com.intellij.openapi.util.WriteExternalException; import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.util.IncorrectOperationException; @@ -135,7 +133,7 @@ public class SeverityRegistrar implements JDOMExternalizable, Comparator list = getOrderAsList(getOrderMap()); for (HighlightSeverity severity : list) { Element info = new Element(INFO_TAG); @@ -437,7 +435,7 @@ public class SeverityRegistrar implements JDOMExternalizable, Comparator - + \ No newline at end of file diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/ConsoleManager.java b/platform/built-in-server/src/org/jetbrains/builtInWebServer/ConsoleManager.java index 1857c7f17f6c..83793c18b6b2 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/ConsoleManager.java +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/ConsoleManager.java @@ -29,7 +29,7 @@ public final class ConsoleManager { } private void createConsole(@NotNull final NetService netService) { - TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(netService.project); + TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(netService.getProject()); netService.configureConsole(consoleBuilder); console = consoleBuilder.getConsole(); @@ -43,8 +43,8 @@ public final class ConsoleManager { toolWindowPanel.setContent(console.getComponent()); toolWindowPanel.setToolbar(toolbar.getComponent()); - ToolWindow toolWindow = ToolWindowManager.getInstance(netService.project) - .registerToolWindow(netService.getConsoleToolWindowId(), false, ToolWindowAnchor.BOTTOM, netService.project, true); + ToolWindow toolWindow = ToolWindowManager.getInstance(netService.getProject()) + .registerToolWindow(netService.getConsoleToolWindowId(), false, ToolWindowAnchor.BOTTOM, netService.getProject(), true); toolWindow.setIcon(netService.getConsoleToolWindowIcon()); Content content = ContentFactory.SERVICE.getInstance().createContent(toolWindowPanel, "", false); @@ -52,6 +52,6 @@ public final class ConsoleManager { toolWindow.getContentManager().addContent(content); } - }, netService.project.getDisposed()); + }, netService.getProject().getDisposed()); } } \ No newline at end of file diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/NetService.java b/platform/built-in-server/src/org/jetbrains/builtInWebServer/NetService.java deleted file mode 100644 index a183cacba659..000000000000 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/NetService.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.jetbrains.builtInWebServer; - -import com.intellij.execution.ExecutionException; -import com.intellij.execution.filters.TextConsoleBuilder; -import com.intellij.execution.process.OSProcessHandler; -import com.intellij.execution.process.ProcessAdapter; -import com.intellij.execution.process.ProcessEvent; -import com.intellij.execution.ui.ConsoleViewContentType; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.ActionGroup; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.util.Consumer; -import com.intellij.util.net.NetUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.concurrency.AsyncPromise; -import org.jetbrains.concurrency.AsyncValueLoader; -import org.jetbrains.concurrency.Promise; - -import javax.swing.*; -import java.io.IOException; - -public abstract class NetService implements Disposable { - protected static final Logger LOG = Logger.getInstance(NetService.class); - - protected final Project project; - private final ConsoleManager consoleManager; - - protected final AsyncValueLoader processHandler = new AsyncValueLoader() { - @Override - protected boolean isCancelOnReject() { - return true; - } - - @Nullable - private OSProcessHandler doGetProcessHandler(int port) { - try { - return createProcessHandler(project, port); - } - catch (ExecutionException e) { - LOG.error(e); - return null; - } - } - - @NotNull - @Override - protected Promise load(@NotNull final AsyncPromise promise) throws IOException { - final int port = NetUtils.findAvailableSocketPort(); - final OSProcessHandler processHandler = doGetProcessHandler(port); - if (processHandler == null) { - promise.setError(Promise.createError("rejected")); - return promise; - } - - promise.rejected(new Consumer() { - @Override - public void consume(Throwable error) { - processHandler.destroyProcess(); - Promise.logError(LOG, error); - } - }); - - final MyProcessAdapter processListener = new MyProcessAdapter(); - processHandler.addProcessListener(processListener); - processHandler.startNotify(); - - if (promise.getState() == Promise.State.REJECTED) { - return promise; - } - - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - @Override - public void run() { - if (promise.getState() != Promise.State.REJECTED) { - try { - connectToProcess(promise, port, processHandler, processListener); - } - catch (Throwable e) { - if (!promise.setError(e)) { - LOG.error(e); - } - } - } - } - }); - return promise; - } - - @Override - protected void disposeResult(@NotNull OSProcessHandler processHandler) { - try { - closeProcessConnections(); - } - finally { - processHandler.destroyProcess(); - } - } - }; - - protected NetService(@NotNull Project project) { - this(project, new ConsoleManager()); - } - - protected NetService(@NotNull Project project, @NotNull ConsoleManager consoleManager) { - this.project = project; - this.consoleManager = consoleManager; - } - - @Nullable - protected abstract OSProcessHandler createProcessHandler(@NotNull Project project, int port) throws ExecutionException; - - protected void connectToProcess(@NotNull AsyncPromise promise, - int port, - @NotNull OSProcessHandler processHandler, - @NotNull Consumer errorOutputConsumer) { - promise.setResult(processHandler); - } - - protected abstract void closeProcessConnections(); - - @Override - public void dispose() { - processHandler.reset(); - } - - protected void configureConsole(@NotNull TextConsoleBuilder consoleBuilder) { - } - - @NotNull - protected abstract String getConsoleToolWindowId(); - - @NotNull - protected abstract Icon getConsoleToolWindowIcon(); - - @NotNull - public ActionGroup getConsoleToolWindowActions() { - return new DefaultActionGroup(); - } - - private final class MyProcessAdapter extends ProcessAdapter implements Consumer { - @Override - public void onTextAvailable(ProcessEvent event, Key outputType) { - print(event.getText(), ConsoleViewContentType.getConsoleViewType(outputType)); - } - - private void print(String text, ConsoleViewContentType contentType) { - consoleManager.getConsole(NetService.this).print(text, contentType); - } - - @Override - public void processTerminated(ProcessEvent event) { - processHandler.reset(); - print(getConsoleToolWindowId() + " terminated\n", ConsoleViewContentType.SYSTEM_OUTPUT); - } - - @Override - public void consume(String message) { - print(message, ConsoleViewContentType.ERROR_OUTPUT); - } - } -} \ No newline at end of file diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/NetService.kt b/platform/built-in-server/src/org/jetbrains/builtInWebServer/NetService.kt new file mode 100644 index 000000000000..d0eb3ae20e57 --- /dev/null +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/NetService.kt @@ -0,0 +1,133 @@ +package org.jetbrains.builtInWebServer + +import com.intellij.execution.ExecutionException +import com.intellij.execution.filters.TextConsoleBuilder +import com.intellij.execution.process.OSProcessHandler +import com.intellij.execution.process.ProcessAdapter +import com.intellij.execution.process.ProcessEvent +import com.intellij.execution.ui.ConsoleViewContentType +import com.intellij.openapi.Disposable +import com.intellij.openapi.actionSystem.ActionGroup +import com.intellij.openapi.actionSystem.DefaultActionGroup +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.util.Consumer +import com.intellij.util.net.NetUtils +import org.jetbrains.concurrency.AsyncPromise +import org.jetbrains.concurrency.AsyncValueLoader +import org.jetbrains.concurrency.Promise +import org.jetbrains.util.concurrency +import org.jetbrains.util.concurrency.toPromise +import javax.swing.Icon + +public abstract class NetService @jvmOverloads protected constructor(protected val project: Project, private val consoleManager: ConsoleManager = ConsoleManager()) : Disposable { + companion object { + protected val LOG: Logger = Logger.getInstance(javaClass()) + } + + protected val processHandler: AsyncValueLoader = object : AsyncValueLoader() { + override fun isCancelOnReject() = true + + private fun doGetProcessHandler(port: Int): OSProcessHandler? { + try { + return createProcessHandler(project, port) + } + catch (e: ExecutionException) { + LOG.error(e) + return null + } + } + + override fun load(promise: AsyncPromise): Promise { + val port = NetUtils.findAvailableSocketPort() + val processHandler = doGetProcessHandler(port) + if (processHandler == null) { + promise.setError("rejected") + return promise + } + + promise.rejected(object : Consumer { + override fun consume(error: Throwable) { + processHandler.destroyProcess() + Promise.logError(LOG, error) + } + }) + + val processListener = MyProcessAdapter() + processHandler.addProcessListener(processListener) + processHandler.startNotify() + + if (promise.getState() === Promise.State.REJECTED) { + return promise + } + + ApplicationManager.getApplication().executeOnPooledThread(object : Runnable { + override fun run() { + if (promise.getState() !== Promise.State.REJECTED) { + try { + connectToProcess(promise.toPromise(), port, processHandler, processListener) + } + catch (e: Throwable) { + if (!promise.setError(e)) { + LOG.error(e) + } + } + } + } + }) + return promise + } + + override fun disposeResult(processHandler: OSProcessHandler) { + try { + closeProcessConnections() + } + finally { + processHandler.destroyProcess() + } + } + } + + throws(ExecutionException::class) + protected abstract fun createProcessHandler(project: Project, port: Int): OSProcessHandler? + + protected open fun connectToProcess(promise: concurrency.AsyncPromise, port: Int, processHandler: OSProcessHandler, errorOutputConsumer: Consumer) { + promise.setResult(processHandler) + } + + protected abstract fun closeProcessConnections() + + override fun dispose() { + processHandler.reset() + } + + protected open fun configureConsole(consoleBuilder: TextConsoleBuilder) { + } + + protected abstract fun getConsoleToolWindowId(): String + + protected abstract fun getConsoleToolWindowIcon(): Icon + + public open fun getConsoleToolWindowActions(): ActionGroup = DefaultActionGroup() + + private inner class MyProcessAdapter : ProcessAdapter(), Consumer { + override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { + print(event.getText(), ConsoleViewContentType.getConsoleViewType(outputType)) + } + + private fun print(text: String, contentType: ConsoleViewContentType) { + consoleManager.getConsole(this@NetService).print(text, contentType) + } + + override fun processTerminated(event: ProcessEvent) { + processHandler.reset() + print("${getConsoleToolWindowId()} terminated\n", ConsoleViewContentType.SYSTEM_OUTPUT) + } + + override fun consume(message: String) { + print(message, ConsoleViewContentType.ERROR_OUTPUT) + } + } +} \ No newline at end of file diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/SingleConnectionNetService.java b/platform/built-in-server/src/org/jetbrains/builtInWebServer/SingleConnectionNetService.java deleted file mode 100644 index 3a7a0f9868f1..000000000000 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/SingleConnectionNetService.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.jetbrains.builtInWebServer; - -import com.intellij.execution.process.OSProcessHandler; -import com.intellij.openapi.project.Project; -import com.intellij.util.Consumer; -import com.intellij.util.net.NetUtils; -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.concurrency.AsyncPromise; -import org.jetbrains.io.NettyUtil; - -import java.net.InetSocketAddress; - -public abstract class SingleConnectionNetService extends NetService { - protected volatile Channel processChannel; - - protected SingleConnectionNetService(@NotNull Project project) { - super(project); - } - - protected abstract void configureBootstrap(@NotNull Bootstrap bootstrap, @NotNull Consumer errorOutputConsumer); - - @Override - protected void connectToProcess(@NotNull AsyncPromise promise, int port, @NotNull OSProcessHandler processHandler, @NotNull Consumer errorOutputConsumer) { - Bootstrap bootstrap = NettyUtil.oioClientBootstrap(); - configureBootstrap(bootstrap, errorOutputConsumer); - Channel channel = NettyUtil.connect(bootstrap, new InetSocketAddress(NetUtils.getLoopbackAddress(), port), promise); - if (channel != null) { - processChannel = channel; - promise.setResult(processHandler); - } - } - - @Override - protected void closeProcessConnections() { - Channel currentProcessChannel = processChannel; - if (currentProcessChannel != null) { - processChannel = null; - NettyUtil.closeAndReleaseFactory(currentProcessChannel); - } - } -} \ No newline at end of file diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/SingleConnectionNetService.kt b/platform/built-in-server/src/org/jetbrains/builtInWebServer/SingleConnectionNetService.kt new file mode 100644 index 000000000000..7f64cd19f582 --- /dev/null +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/SingleConnectionNetService.kt @@ -0,0 +1,36 @@ +package org.jetbrains.builtInWebServer + +import com.intellij.execution.process.OSProcessHandler +import com.intellij.openapi.project.Project +import com.intellij.util.Consumer +import com.intellij.util.net.NetUtils +import io.netty.bootstrap.Bootstrap +import io.netty.channel.Channel +import org.jetbrains.util.concurrency.AsyncPromise +import org.jetbrains.io.NettyUtil + +import java.net.InetSocketAddress + +public abstract class SingleConnectionNetService(project: Project) : NetService(project) { + protected volatile var processChannel: Channel? = null + + protected abstract fun configureBootstrap(bootstrap: Bootstrap, errorOutputConsumer: Consumer) + + override fun connectToProcess(promise: AsyncPromise, port: Int, processHandler: OSProcessHandler, errorOutputConsumer: Consumer) { + val bootstrap = NettyUtil.oioClientBootstrap() + configureBootstrap(bootstrap, errorOutputConsumer) + val channel = NettyUtil.connect(bootstrap, InetSocketAddress(NetUtils.getLoopbackAddress(), port), promise) + if (channel != null) { + processChannel = channel + promise.setResult(processHandler) + } + } + + override fun closeProcessConnections() { + val currentProcessChannel = processChannel + if (currentProcessChannel != null) { + processChannel = null + NettyUtil.closeAndReleaseFactory(currentProcessChannel) + } + } +} \ No newline at end of file diff --git a/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiService.java b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiService.java index 1ee56c71f2c8..a431a729407b 100644 --- a/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiService.java +++ b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiService.java @@ -102,15 +102,15 @@ public abstract class FastCgiService extends SingleConnectionNetService { } try { - if (processHandler.has()) { - fastCgiRequest.writeToServerChannel(notEmptyContent, processChannel); + if (getProcessHandler().has()) { + fastCgiRequest.writeToServerChannel(notEmptyContent, getProcessChannel()); } else { - processHandler.get() + getProcessHandler().get() .done(new Consumer() { @Override public void consume(OSProcessHandler osProcessHandler) { - fastCgiRequest.writeToServerChannel(notEmptyContent, processChannel); + fastCgiRequest.writeToServerChannel(notEmptyContent, getProcessChannel()); } }) .rejected(new Consumer() { diff --git a/platform/built-in-server/src/org/jetbrains/io/jsonRpc/Client.java b/platform/built-in-server/src/org/jetbrains/io/jsonRpc/Client.java index 3922374d5f07..9c66ed10700a 100644 --- a/platform/built-in-server/src/org/jetbrains/io/jsonRpc/Client.java +++ b/platform/built-in-server/src/org/jetbrains/io/jsonRpc/Client.java @@ -90,7 +90,12 @@ public abstract class Client extends UserDataHolderBase { public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { Throwable cause = future.cause(); - setError(cause == null ? Promise.createError("No success") : cause); + if (cause == null) { + setError("No success"); + } + else { + setError(cause); + } } } } diff --git a/platform/built-in-server/src/org/jetbrains/io/jsonRpc/JsonRpcServer.java b/platform/built-in-server/src/org/jetbrains/io/jsonRpc/JsonRpcServer.java index 66ee970f2e2c..4e6f18ce03d0 100644 --- a/platform/built-in-server/src/org/jetbrains/io/jsonRpc/JsonRpcServer.java +++ b/platform/built-in-server/src/org/jetbrains/io/jsonRpc/JsonRpcServer.java @@ -109,7 +109,7 @@ public class JsonRpcServer implements MessageServer { promise.setResult(JsonUtil.nextAny(reader)); } else { - promise.setError(Promise.createError("error")); + promise.setError("error"); } return; } diff --git a/platform/configuration-store-impl/src/DirectoryBasedStorage.kt b/platform/configuration-store-impl/src/DirectoryBasedStorage.kt index 5800cb509139..0208009ca758 100644 --- a/platform/configuration-store-impl/src/DirectoryBasedStorage.kt +++ b/platform/configuration-store-impl/src/DirectoryBasedStorage.kt @@ -20,9 +20,13 @@ import com.intellij.openapi.components.StateSplitter import com.intellij.openapi.components.StateSplitterEx import com.intellij.openapi.components.StateStorage import com.intellij.openapi.components.TrackingPathMacroSubstitutor -import com.intellij.openapi.components.impl.stores.* +import com.intellij.openapi.components.impl.stores.DirectoryStorageUtil +import com.intellij.openapi.components.impl.stores.FileStorageCoreUtil +import com.intellij.openapi.components.impl.stores.StateStorageBase import com.intellij.openapi.components.store.ReadOnlyModificationException import com.intellij.openapi.util.JDOMUtil +import com.intellij.openapi.util.Pair +import com.intellij.openapi.vfs.CharsetToolkit import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.ArrayUtil @@ -35,6 +39,7 @@ import org.jdom.Element import java.io.File import java.io.FileNotFoundException import java.io.IOException +import java.nio.ByteBuffer open class DirectoryBasedStorage(private val myPathMacroSubstitutor: TrackingPathMacroSubstitutor?, private val myDir: File, private val mySplitter: StateSplitter) : StateStorageBase>() { private volatile var virtualFile: VirtualFile? = null @@ -128,21 +133,21 @@ open class DirectoryBasedStorage(private val myPathMacroSubstitutor: TrackingPat var dir = storage.getVirtualFile() if (copiedStorageData!!.isEmpty()) { if (dir != null && dir.exists()) { - StorageUtil.deleteFile(this, dir) + deleteFile(this, dir) } storage.setStorageData(copiedStorageData!!) return } if (dir == null || !dir.isValid()) { - dir = StorageUtil.createDir(storage.myDir, this) + dir = createDir(storage.myDir, this) storage.virtualFile = dir } if (!dirtyFileNames.isEmpty()) { - saveStates(dir!!) + saveStates(dir) } - if (dir!!.exists() && !removedFileNames.isEmpty()) { + if (dir.exists() && !removedFileNames.isEmpty()) { deleteFiles(dir) } @@ -166,9 +171,9 @@ open class DirectoryBasedStorage(private val myPathMacroSubstitutor: TrackingPat storeElement.setAttribute(FileStorageCoreUtil.NAME, componentNameToFileNameToStates.getKey()) storeElement.addContent(element) - val file = StorageUtil.getFile(fileName, dir, this) + val file = getFile(fileName, dir, this) // we don't write xml prolog due to historical reasons (and should not in any case) - StorageUtil.writeFile(null, this, file, storeElement, LineSeparator.fromString(if (file.exists()) StorageUtil.loadFile(file).second else SystemProperties.getLineSeparator()), false) + writeFile(null, this, file, storeElement, LineSeparator.fromString(if (file.exists()) loadFile(file).second else SystemProperties.getLineSeparator()), false) } catch (e: IOException) { StateStorageBase.LOG.error(e) @@ -353,4 +358,22 @@ open class DirectoryBasedStorage(private val myPathMacroSubstitutor: TrackingPat fileToState.put(fileName, state) } } +} + +private val NON_EXISTENT_FILE_DATA = Pair.create(null, SystemProperties.getLineSeparator()) + +/** + * @return pair.first - file contents (null if file does not exist), pair.second - file line separators + */ +private fun loadFile(file: VirtualFile?): Pair { + if (file == null || !file.exists()) { + return NON_EXISTENT_FILE_DATA + } + + val bytes = file.contentsToByteArray() + var lineSeparator: String? = file.getDetectedLineSeparator() + if (lineSeparator == null) { + lineSeparator = detectLineSeparators(CharsetToolkit.UTF8_CHARSET.decode(ByteBuffer.wrap(bytes)), null).getSeparatorString() + } + return Pair.create(bytes, lineSeparator) } \ No newline at end of file diff --git a/platform/configuration-store-impl/src/FileBasedStorage.kt b/platform/configuration-store-impl/src/FileBasedStorage.kt index afdc850b0f16..920e45c6b327 100644 --- a/platform/configuration-store-impl/src/FileBasedStorage.kt +++ b/platform/configuration-store-impl/src/FileBasedStorage.kt @@ -19,20 +19,30 @@ import com.intellij.notification.Notification import com.intellij.notification.NotificationType import com.intellij.notification.Notifications import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.WriteAction +import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.components.RoamingType +import com.intellij.openapi.components.StateStorage import com.intellij.openapi.components.StoragePathMacros import com.intellij.openapi.components.TrackingPathMacroSubstitutor import com.intellij.openapi.components.impl.stores.FileStorage import com.intellij.openapi.components.impl.stores.StorageUtil +import com.intellij.openapi.components.store.ReadOnlyModificationException +import com.intellij.openapi.fileEditor.impl.LoadTextUtil 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.io.systemIndependentPath import com.intellij.openapi.vfs.CharsetToolkit import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.ArrayUtil import com.intellij.util.LineSeparator import org.jdom.Element import org.jdom.JDOMException +import org.jdom.Parent import java.io.File +import java.io.FileNotFoundException import java.io.IOException import java.nio.ByteBuffer @@ -81,11 +91,11 @@ open class FileBasedStorage(file: File, val virtualFile = storage.getVirtualFile() if (element == null) { - StorageUtil.deleteFile(storage.file, this, virtualFile) + deleteFile(storage.file, this, virtualFile) storage.cachedVirtualFile = null } else { - storage.cachedVirtualFile = StorageUtil.writeFile(storage.file, this, virtualFile, element, if (storage.isUseXmlProlog) storage.lineSeparator!! else LineSeparator.LF, storage.isUseXmlProlog) + storage.cachedVirtualFile = writeFile(storage.file, this, virtualFile, element, if (storage.isUseXmlProlog) storage.lineSeparator!! else LineSeparator.LF, storage.isUseXmlProlog) } } } @@ -113,7 +123,7 @@ open class FileBasedStorage(file: File, } else { val charBuffer = CharsetToolkit.UTF8_CHARSET.decode(ByteBuffer.wrap(file.contentsToByteArray())) - lineSeparator = StorageUtil.detectLineSeparators(charBuffer, if (isUseXmlProlog) null else LineSeparator.LF) + lineSeparator = detectLineSeparators(charBuffer, if (isUseXmlProlog) null else LineSeparator.LF) return JDOMUtil.loadDocument(charBuffer).detachRootElement() } } @@ -128,7 +138,7 @@ open class FileBasedStorage(file: File, private fun processReadException(e: Exception?) { val contentTruncated = e == null - blockSavingTheContent = !contentTruncated && (StorageUtil.isProjectOrModuleFile(fileSpec) || fileSpec == StoragePathMacros.WORKSPACE_FILE) + blockSavingTheContent = !contentTruncated && (isProjectOrModuleFile(fileSpec) || fileSpec == StoragePathMacros.WORKSPACE_FILE) if (!ApplicationManager.getApplication().isUnitTestMode() && !ApplicationManager.getApplication().isHeadlessEnvironment()) { if (e != null) { LOG.info(e) @@ -139,3 +149,134 @@ open class FileBasedStorage(file: File, override fun toString() = file.systemIndependentPath } + +fun writeFile(file: File?, requestor: Any, virtualFile: VirtualFile?, element: Element, lineSeparator: LineSeparator, prependXmlProlog: Boolean): VirtualFile { + val result = if (file != null && (virtualFile == null || !virtualFile.isValid())) { + StorageUtil.getOrCreateVirtualFile(requestor, file) + } + else { + virtualFile!! + } + + if (LOG.isDebugEnabled() || ApplicationManager.getApplication().isUnitTestMode()) { + val content = element.toBufferExposingByteArray(lineSeparator.getSeparatorString()) + if (isEqualContent(result, lineSeparator, content, prependXmlProlog)) { + throw IllegalStateException("Content equals, but it must be handled not on this level: ${result.getName()}") + } + else if (StorageUtil.DEBUG_LOG != null && ApplicationManager.getApplication().isUnitTestMode()) { + StorageUtil.DEBUG_LOG = "${result.getPath()}:\n$content\nOld Content:\n${LoadTextUtil.loadText(result)}\n---------" + } + } + + doWrite(requestor, result, element, lineSeparator, prependXmlProlog) + return result +} + +private val XML_PROLOG = "".toByteArray() + +private fun isEqualContent(result: VirtualFile, lineSeparator: LineSeparator, content: BufferExposingByteArrayOutputStream, prependXmlProlog: Boolean): Boolean { + val headerLength = if (!prependXmlProlog) 0 else XML_PROLOG.size() + lineSeparator.getSeparatorBytes().size() + if (result.getLength().toInt() != (headerLength + content.size())) { + return false + } + + val oldContent = result.contentsToByteArray() + + if (prependXmlProlog && (!ArrayUtil.startsWith(oldContent, XML_PROLOG) || !ArrayUtil.startsWith(oldContent, XML_PROLOG.size(), lineSeparator.getSeparatorBytes()))) { + return false + } + + for (i in headerLength..oldContent.size() - 1) { + if (oldContent[i] != content.getInternalBuffer()[i - headerLength]) { + return false + } + } + return true +} + +private fun doWrite(requestor: Any, file: VirtualFile, content: Any, lineSeparator: LineSeparator, prependXmlProlog: Boolean) { + if (LOG.isDebugEnabled()) { + LOG.debug("Save ${file.getPresentableUrl()}") + } + + val token = WriteAction.start() + try { + val out = file.getOutputStream(requestor) + try { + if (prependXmlProlog) { + out.write(XML_PROLOG) + out.write(lineSeparator.getSeparatorBytes()) + } + if (content is Element) { + JDOMUtil.writeParent(content, out, lineSeparator.getSeparatorString()) + } + else { + (content as BufferExposingByteArrayOutputStream).writeTo(out) + } + } + finally { + out.close() + } + } + catch (e: FileNotFoundException) { + // may be element is not long-lived, so, we must write it to byte array + val byteArray = if (content is Element) content.toBufferExposingByteArray(lineSeparator.getSeparatorString()) else (content as BufferExposingByteArrayOutputStream) + throw ReadOnlyModificationException(file, e, object : StateStorage.SaveSession { + override fun save() { + doWrite(requestor, file, byteArray, lineSeparator, prependXmlProlog) + } + }) + } + finally { + token.finish() + } +} + +fun Parent.toBufferExposingByteArray(lineSeparator: String = "\n"): BufferExposingByteArrayOutputStream { + val out = BufferExposingByteArrayOutputStream(512) + JDOMUtil.writeParent(this, out, lineSeparator) + return out +} + +public fun isProjectOrModuleFile(fileSpec: String): Boolean = StoragePathMacros.PROJECT_FILE == fileSpec || fileSpec.startsWith(StoragePathMacros.PROJECT_CONFIG_DIR) || fileSpec == StoragePathMacros.MODULE_FILE + +fun detectLineSeparators(chars: CharSequence, defaultSeparator: LineSeparator?): LineSeparator { + for (c in chars) { + if (c == '\r') { + return LineSeparator.CRLF + } + else if (c == '\n') { + // if we are here, there was no \r before + return LineSeparator.LF + } + } + return defaultSeparator ?: LineSeparator.getSystemLineSeparator() +} + +private fun deleteFile(file: File, requestor: Any, virtualFile: VirtualFile?) { + if (virtualFile == null) { + LOG.warn("Cannot find virtual file ${file.getAbsolutePath()}") + } + + if (virtualFile == null) { + if (file.exists()) { + FileUtil.delete(file) + } + } + else if (virtualFile.exists()) { + try { + deleteFile(requestor, virtualFile) + } + catch (e: FileNotFoundException) { + throw ReadOnlyModificationException(virtualFile, e, object : StateStorage.SaveSession { + override fun save() { + deleteFile(requestor, virtualFile) + } + }) + } + } +} + +fun deleteFile(requestor: Any, virtualFile: VirtualFile) { + runWriteAction { virtualFile.delete(requestor) } +} \ No newline at end of file diff --git a/platform/configuration-store-impl/src/SchemeManagerImpl.kt b/platform/configuration-store-impl/src/SchemeManagerImpl.kt index 0981b39876f7..03e5d3f1b392 100644 --- a/platform/configuration-store-impl/src/SchemeManagerImpl.kt +++ b/platform/configuration-store-impl/src/SchemeManagerImpl.kt @@ -27,6 +27,7 @@ import com.intellij.openapi.components.impl.stores.StorageUtil import com.intellij.openapi.components.service import com.intellij.openapi.extensions.AbstractExtensionPointBean import com.intellij.openapi.options.* +import com.intellij.openapi.project.ProjectBundle import com.intellij.openapi.util.Comparing import com.intellij.openapi.util.Condition import com.intellij.openapi.util.JDOMUtil @@ -49,6 +50,7 @@ import org.jdom.Document import org.jdom.Element import java.io.File import java.io.FilenameFilter +import java.io.IOException import java.io.InputStream import java.util.ArrayList import java.util.Collections @@ -528,7 +530,7 @@ public class SchemeManagerImpl(private val filesToDelete.remove(fileName) // stream provider always use LF separator - val byteOut = StorageUtil.writeToBytes(element, "\n") + val byteOut = element.toBufferExposingByteArray() var providerPath: String? if (provider != null && provider.enabled) { @@ -548,12 +550,12 @@ public class SchemeManagerImpl(private val var file: VirtualFile? = null var dir = getDirectory() if (dir == null || !dir.isValid()) { - dir = StorageUtil.createDir(ioDirectory, this) + dir = createDir(ioDirectory, this) directory = dir } if (renamed) { - file = dir!!.findChild(externalInfo!!.fileName) + file = dir.findChild(externalInfo!!.fileName) if (file != null) { runWriteAction { file!!.rename(this, fileName) @@ -562,7 +564,7 @@ public class SchemeManagerImpl(private val } if (file == null) { - file = StorageUtil.getFile(fileName, dir!!, this) + file = getFile(fileName, dir, this) } runWriteAction { @@ -873,4 +875,20 @@ private inline fun MutableList.catch(runnable: () -> Unit) { } inline val Scheme.name: String - get() = getName() \ No newline at end of file + get() = getName() + + +fun createDir(ioDir: File, requestor: Any): VirtualFile { + ioDir.mkdirs() + val parentFile = ioDir.getParent() + val parentVirtualFile = (if (parentFile == null) null else VfsUtil.createDirectoryIfMissing(parentFile)) ?: throw IOException(ProjectBundle.message("project.configuration.save.file.not.found", parentFile)) + return getFile(ioDir.getName(), parentVirtualFile, requestor) +} + +fun getFile(fileName: String, parent: VirtualFile, requestor: Any): VirtualFile { + val file = parent.findChild(fileName) + if (file != null) { + return file + } + return runWriteAction { parent.createChildData(requestor, fileName) } +} \ No newline at end of file diff --git a/platform/configuration-store-impl/src/StreamProvider.kt b/platform/configuration-store-impl/src/StreamProvider.kt index ec4738a81f4a..576d8178ac58 100644 --- a/platform/configuration-store-impl/src/StreamProvider.kt +++ b/platform/configuration-store-impl/src/StreamProvider.kt @@ -16,6 +16,7 @@ package com.intellij.configurationStore import com.intellij.openapi.components.RoamingType +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream import java.io.InputStream public interface StreamProvider { @@ -44,6 +45,10 @@ public interface StreamProvider { public fun delete(fileSpec: String, roamingType: RoamingType = RoamingType.DEFAULT) } -public fun StreamProvider.write(fileSpec: String, content: String) { - write(fileSpec, content.toByteArray()) +public fun StreamProvider.write(path: String, content: String) { + write(path, content.toByteArray()) +} + +public fun StreamProvider.write(path: String, content: BufferExposingByteArrayOutputStream, roamingType: RoamingType = RoamingType.DEFAULT) { + write(path, content.getInternalBuffer(), content.size(), roamingType) } \ No newline at end of file diff --git a/platform/configuration-store-impl/src/XmlElementStorage.kt b/platform/configuration-store-impl/src/XmlElementStorage.kt index b40a7ae645f3..a12ac2d52dec 100644 --- a/platform/configuration-store-impl/src/XmlElementStorage.kt +++ b/platform/configuration-store-impl/src/XmlElementStorage.kt @@ -20,7 +20,6 @@ import com.intellij.openapi.components.StateStorage import com.intellij.openapi.components.TrackingPathMacroSubstitutor import com.intellij.openapi.components.impl.stores.FileStorageCoreUtil import com.intellij.openapi.components.impl.stores.StateStorageBase -import com.intellij.openapi.components.impl.stores.StorageUtil import com.intellij.openapi.util.JDOMUtil import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.SmartHashSet @@ -140,8 +139,7 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S } else { // we should use standard line-separator (\n) - stream provider can share file content on any OS - val content = StorageUtil.writeToBytes(element, "\n") - provider.write(storage.fileSpec, content.getInternalBuffer(), content.size(), storage.roamingType) + provider.write(storage.fileSpec, element.toBufferExposingByteArray(), storage.roamingType) } } else { diff --git a/platform/core-api/src/com/intellij/openapi/editor/colors/TextAttributesKey.java b/platform/core-api/src/com/intellij/openapi/editor/colors/TextAttributesKey.java index 879685b8075d..008e922337ec 100644 --- a/platform/core-api/src/com/intellij/openapi/editor/colors/TextAttributesKey.java +++ b/platform/core-api/src/com/intellij/openapi/editor/colors/TextAttributesKey.java @@ -20,7 +20,6 @@ import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.util.JDOMExternalizerUtil; import com.intellij.openapi.util.NullableLazyValue; import com.intellij.openapi.util.VolatileNullableLazyValue; -import com.intellij.openapi.util.WriteExternalException; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.containers.ContainerUtil; import org.jdom.Element; @@ -91,7 +90,7 @@ public final class TextAttributesKey implements Comparable { return find(externalName); } - public void writeExternal(Element element) throws WriteExternalException { + public void writeExternal(Element element) { JDOMExternalizerUtil.writeField(element, "myExternalName", myExternalName); if (myDefaultAttributes != NULL_ATTRIBUTES) { @@ -100,7 +99,6 @@ public final class TextAttributesKey implements Comparable { } } - public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; diff --git a/platform/core-api/src/com/intellij/psi/SyntaxTraverser.java b/platform/core-api/src/com/intellij/psi/SyntaxTraverser.java index 785de7e4b5ad..cff07563ccaa 100644 --- a/platform/core-api/src/com/intellij/psi/SyntaxTraverser.java +++ b/platform/core-api/src/com/intellij/psi/SyntaxTraverser.java @@ -47,6 +47,11 @@ public class SyntaxTraverser extends FilteredTraverserBase(psiApi(), null); } + @NotNull + public static SyntaxTraverser psiTraverser(@Nullable PsiElement root) { + return psiTraverser().withRoot(root); + } + @NotNull public static SyntaxTraverser revPsiTraverser() { return new SyntaxTraverser(psiApiReversed(), null); @@ -57,6 +62,11 @@ public class SyntaxTraverser extends FilteredTraverserBase(astApi(), null); } + @NotNull + public static SyntaxTraverser astTraverser(@Nullable ASTNode root) { + return astTraverser().withRoot(root); + } + @NotNull public static SyntaxTraverser lightTraverser(@NotNull PsiBuilder builder) { LighterASTApi api = new LighterASTApi(builder); diff --git a/platform/core-api/src/org/jetbrains/concurrency/AsyncPromise.java b/platform/core-api/src/org/jetbrains/concurrency/AsyncPromise.java index 3f45fc922878..4f1a5149e31f 100644 --- a/platform/core-api/src/org/jetbrains/concurrency/AsyncPromise.java +++ b/platform/core-api/src/org/jetbrains/concurrency/AsyncPromise.java @@ -319,6 +319,10 @@ public class AsyncPromise extends Promise implements Getter { return consumer instanceof Obsolescent && ((Obsolescent)consumer).isObsolete(); } + public boolean setError(@NotNull String error) { + return setError(Promise.createError(error)); + } + public boolean setError(@NotNull Throwable error) { if (state != State.PENDING) { return false; diff --git a/platform/core-api/src/org/jetbrains/concurrency/AsyncValueLoader.java b/platform/core-api/src/org/jetbrains/concurrency/AsyncValueLoader.java index 0a458079349e..4029f9182e31 100644 --- a/platform/core-api/src/org/jetbrains/concurrency/AsyncValueLoader.java +++ b/platform/core-api/src/org/jetbrains/concurrency/AsyncValueLoader.java @@ -57,7 +57,7 @@ public abstract class AsyncValueLoader { private void rejectAndDispose(@NotNull AsyncPromise asyncResult) { try { - asyncResult.setError(Promise.createError("rejected")); + asyncResult.setError("rejected"); } finally { T result = asyncResult.get(); diff --git a/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java b/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java index 2a864945bba1..d8266b3f4ba7 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java @@ -223,6 +223,10 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { myDescriptionChildText = pluginBean.description; myChangeNotes = pluginBean.changeNotes; myVersion = pluginBean.pluginVersion; + if (myVersion == null) { + myVersion = PluginManagerCore.getBuildNumber().getBaselineVersion() + ".SNAPSHOT"; + } + myCategory = pluginBean.category; diff --git a/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java b/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java index 3489ce3de3d8..edf0bbeae10d 100644 --- a/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java +++ b/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java @@ -16,6 +16,7 @@ package com.intellij.psi; import com.google.common.util.concurrent.Atomics; +import com.intellij.injected.editor.DocumentWindow; import com.intellij.lang.Language; import com.intellij.lang.LanguageParserDefinitions; import com.intellij.lang.ParserDefinition; @@ -542,6 +543,9 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi private void checkLengthConsistency() { Document document = getCachedDocument(); + if (document instanceof DocumentWindow) { + return; + } if (document != null && ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(myManager.getProject())).getSynchronizer().isInSynchronization(document)) { return; diff --git a/platform/diff-api/src/com/intellij/diff/DiffContentFactory.java b/platform/diff-api/src/com/intellij/diff/DiffContentFactory.java index bd41446c1c1f..8dd004c11532 100644 --- a/platform/diff-api/src/com/intellij/diff/DiffContentFactory.java +++ b/platform/diff-api/src/com/intellij/diff/DiffContentFactory.java @@ -50,6 +50,9 @@ public abstract class DiffContentFactory { @NotNull public abstract DocumentContent create(@NotNull String text, @Nullable FileType type, boolean respectLineSeparators); + @NotNull + public abstract DocumentContent create(@NotNull String text, @Nullable VirtualFile highlightFile); + @NotNull public abstract DocumentContent create(@Nullable Project project, @NotNull Document document); diff --git a/platform/diff-api/src/com/intellij/diff/DiffDialogHints.java b/platform/diff-api/src/com/intellij/diff/DiffDialogHints.java index b44548d1e924..c921ff64f97c 100644 --- a/platform/diff-api/src/com/intellij/diff/DiffDialogHints.java +++ b/platform/diff-api/src/com/intellij/diff/DiffDialogHints.java @@ -16,11 +16,9 @@ package com.intellij.diff; import com.intellij.openapi.ui.WindowWrapper; -import com.intellij.openapi.util.BooleanGetter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import java.awt.*; public class DiffDialogHints { @@ -35,8 +33,6 @@ public class DiffDialogHints { @Nullable private final WindowWrapper.Mode myMode; @Nullable private Component myParent; - @Nullable private BooleanGetter myOkAction; - @Nullable private BooleanGetter myCancelAction; public DiffDialogHints(@Nullable WindowWrapper.Mode mode) { this(mode, null); @@ -51,14 +47,6 @@ public class DiffDialogHints { myParent = value; } - public void setOkAction(@Nullable BooleanGetter value) { - myOkAction = value; - } - - public void setCancelAction(@Nullable BooleanGetter value) { - myCancelAction = value; - } - // // Getters // @@ -72,14 +60,4 @@ public class DiffDialogHints { public Component getParent() { return myParent; } - - @Nullable - public BooleanGetter getOkAction() { - return myOkAction; - } - - @Nullable - public BooleanGetter getCancelAction() { - return myCancelAction; - } } diff --git a/platform/diff-impl/src/com/intellij/diff/DiffContentFactoryImpl.java b/platform/diff-impl/src/com/intellij/diff/DiffContentFactoryImpl.java index 653017dcf568..d6f207e2ad93 100644 --- a/platform/diff-impl/src/com/intellij/diff/DiffContentFactoryImpl.java +++ b/platform/diff-impl/src/com/intellij/diff/DiffContentFactoryImpl.java @@ -58,7 +58,7 @@ public class DiffContentFactoryImpl extends DiffContentFactory { @Override @NotNull public DocumentContent create(@NotNull String text) { - return create(text, null); + return create(text, (FileType)null); } @Override @@ -73,6 +73,11 @@ public class DiffContentFactoryImpl extends DiffContentFactory { return createImpl(text, type, null, null, respectLineSeparators, true); } + @NotNull + public DocumentContent create(@NotNull String text, @Nullable VirtualFile highlightFile) { + return createImpl(text, highlightFile != null ? highlightFile.getFileType() : null, highlightFile, null, true, true); + } + @Override @NotNull public DocumentContent create(@Nullable Project project, @NotNull Document document) { diff --git a/platform/diff-impl/src/com/intellij/diff/actions/impl/FocusOppositePaneAction.java b/platform/diff-impl/src/com/intellij/diff/actions/impl/FocusOppositePaneAction.java index 3415e681255f..982623b5fe0a 100644 --- a/platform/diff-impl/src/com/intellij/diff/actions/impl/FocusOppositePaneAction.java +++ b/platform/diff-impl/src/com/intellij/diff/actions/impl/FocusOppositePaneAction.java @@ -33,6 +33,8 @@ public class FocusOppositePaneAction extends AnAction implements DumbAware { public FocusOppositePaneAction(boolean scrollToPosition) { myScrollToPosition = scrollToPosition; setEnabledInModalContext(true); + + EmptyAction.setupAction(this, getActionId(), null); } @Override @@ -41,7 +43,11 @@ public class FocusOppositePaneAction extends AnAction implements DumbAware { } public void setupAction(@NotNull JComponent component) { - String action = myScrollToPosition ? "Diff.FocusOppositePaneAndScroll" : "Diff.FocusOppositePane"; - EmptyAction.setupAction(this, action, component); + registerCustomShortcutSet(getShortcutSet(), component); + } + + @NotNull + private String getActionId() { + return myScrollToPosition ? "Diff.FocusOppositePaneAndScroll" : "Diff.FocusOppositePane"; } } \ No newline at end of file diff --git a/platform/diff-impl/src/com/intellij/diff/impl/DiffWindowBase.java b/platform/diff-impl/src/com/intellij/diff/impl/DiffWindowBase.java index 424c2fa68e84..06bcacc16fe0 100644 --- a/platform/diff-impl/src/com/intellij/diff/impl/DiffWindowBase.java +++ b/platform/diff-impl/src/com/intellij/diff/impl/DiffWindowBase.java @@ -63,8 +63,6 @@ public abstract class DiffWindowBase { myProcessor.requestFocus(); // TODO: not needed for modal dialogs. Make a flag in WindowWrapperBuilder ? } }) - .setOkAction(myHints.getOkAction()) - .setCancelAction(myHints.getCancelAction()) .build(); myWrapper.setImage(ImageLoader.loadFromResource("/diff/Diff.png")); Disposer.register(myWrapper, myProcessor); diff --git a/platform/diff-impl/src/com/intellij/diff/merge/BinaryMergeTool.java b/platform/diff-impl/src/com/intellij/diff/merge/BinaryMergeTool.java index 76df64d8e1b0..f9e11c87bc14 100644 --- a/platform/diff-impl/src/com/intellij/diff/merge/BinaryMergeTool.java +++ b/platform/diff-impl/src/com/intellij/diff/merge/BinaryMergeTool.java @@ -23,8 +23,6 @@ import com.intellij.diff.requests.DiffRequest; import com.intellij.diff.requests.SimpleDiffRequest; import com.intellij.diff.tools.binary.ThreesideBinaryDiffViewer; import com.intellij.diff.tools.holders.BinaryEditorHolder; -import com.intellij.openapi.diff.DiffBundle; -import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.BooleanGetter; import com.intellij.openapi.util.Disposer; import com.intellij.util.containers.ContainerUtil; @@ -116,9 +114,7 @@ public class BinaryMergeTool implements MergeTool { components.closeHandler = new BooleanGetter() { @Override public boolean get() { - return Messages.showYesNoDialog(getComponent().getRootPane(), - DiffBundle.message("merge.dialog.exit.without.applying.changes.confirmation.message"), - DiffBundle.message("cancel.visual.merge.dialog.title"), Messages.getQuestionIcon()) == Messages.YES; + return MergeUtil.showExitWithoutApplyingChangesDialog(getComponent(), myMergeRequest, myMergeContext); } }; @@ -134,14 +130,10 @@ public class BinaryMergeTool implements MergeTool { return new AbstractAction(caption) { @Override public void actionPerformed(ActionEvent e) { - if (result == MergeResult.CANCEL) { - if (Messages.showYesNoDialog(getComponent().getRootPane(), - DiffBundle.message("merge.dialog.exit.without.applying.changes.confirmation.message"), - DiffBundle.message("cancel.visual.merge.dialog.title"), Messages.getQuestionIcon()) != Messages.YES) { - return; - } + if (result == MergeResult.CANCEL && + !MergeUtil.showExitWithoutApplyingChangesDialog(getComponent(), myMergeRequest, myMergeContext)) { + return; } - myMergeContext.finishMerge(result); } }; diff --git a/platform/diff-impl/src/com/intellij/diff/merge/MergeUtil.java b/platform/diff-impl/src/com/intellij/diff/merge/MergeUtil.java index 36b5e6d48073..004915250a35 100644 --- a/platform/diff-impl/src/com/intellij/diff/merge/MergeUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/merge/MergeUtil.java @@ -17,13 +17,16 @@ package com.intellij.diff.merge; import com.intellij.diff.DiffContext; import com.intellij.diff.util.ThreeSide; +import com.intellij.openapi.diff.DiffBundle; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.util.List; public class MergeUtil { @@ -90,4 +93,11 @@ public class MergeUtil { myMergeContext.putUserData(key, value); } } + + public static boolean showExitWithoutApplyingChangesDialog(@NotNull JComponent component, @NotNull MergeRequest request, @NotNull MergeContext context) { + return Messages.showYesNoDialog(component.getRootPane(), + DiffBundle.message("merge.dialog.exit.without.applying.changes.confirmation.message"), + DiffBundle.message("cancel.visual.merge.dialog.title"), Messages.getQuestionIcon()) == + Messages.YES; + } } diff --git a/platform/diff-impl/src/com/intellij/diff/merge/TextMergeTool.java b/platform/diff-impl/src/com/intellij/diff/merge/TextMergeTool.java index a82a03b741c4..eb775e9ceb69 100644 --- a/platform/diff-impl/src/com/intellij/diff/merge/TextMergeTool.java +++ b/platform/diff-impl/src/com/intellij/diff/merge/TextMergeTool.java @@ -30,6 +30,7 @@ import com.intellij.diff.requests.SimpleDiffRequest; import com.intellij.diff.tools.simple.ThreesideTextDiffViewerEx; import com.intellij.diff.tools.util.DiffNotifications; import com.intellij.diff.tools.util.KeyboardModifierListener; +import com.intellij.diff.tools.util.base.TextDiffViewerUtil; import com.intellij.diff.util.*; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.*; @@ -165,9 +166,7 @@ public class TextMergeTool implements MergeTool { components.closeHandler = new BooleanGetter() { @Override public boolean get() { - return Messages.showYesNoDialog(getComponent().getRootPane(), - DiffBundle.message("merge.dialog.exit.without.applying.changes.confirmation.message"), - DiffBundle.message("cancel.visual.merge.dialog.title"), Messages.getQuestionIcon()) == Messages.YES; + return MergeUtil.showExitWithoutApplyingChangesDialog(getComponent(), myMergeRequest, myMergeContext); } }; @@ -273,9 +272,9 @@ public class TextMergeTool implements MergeTool { group.add(new ApplySelectedChangesAction(Side.RIGHT, false)); group.add(new IgnoreSelectedChangesAction(Side.LEFT, false)); group.add(new IgnoreSelectedChangesAction(Side.RIGHT, false)); - group.add(Separator.getInstance()); - group.addAll(super.createEditorPopupActions()); + group.add(Separator.getInstance()); + group.addAll(TextDiffViewerUtil.createEditorPopupActions()); return group; } @@ -312,12 +311,9 @@ public class TextMergeTool implements MergeTool { return; } } - if (result == MergeResult.CANCEL) { - if (Messages.showYesNoDialog(myPanel.getRootPane(), - DiffBundle.message("merge.dialog.exit.without.applying.changes.confirmation.message"), - DiffBundle.message("cancel.visual.merge.dialog.title"), Messages.getQuestionIcon()) != Messages.YES) { - return; - } + if (result == MergeResult.CANCEL && + !MergeUtil.showExitWithoutApplyingChangesDialog(getComponent(), myMergeRequest, myMergeContext)) { + return; } destroyChangedBlocks(); myMergeContext.finishMerge(result); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java index 0d075f734268..f249351111e2 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java @@ -67,6 +67,12 @@ public class ExternalDiffToolUtil { } else if (content instanceof FileContent) { VirtualFile file = ((FileContent)content).getFile(); + + Document document = FileDocumentManager.getInstance().getCachedDocument(file); + if (document != null) { + FileDocumentManager.getInstance().saveDocument(document); + } + if (file.isInLocalFileSystem()) { return file.getPath(); } @@ -123,14 +129,20 @@ public class ExternalDiffToolUtil { @NotNull public static OutputFile createOutputFile(@NotNull DiffContent content, @Nullable String windowTitle) throws IOException { if (content instanceof FileContent) { - FileContent fileContent = (FileContent)content; - if (fileContent.getFile().isInLocalFileSystem()) { - return new LocalOutputFile(fileContent.getFile()); + VirtualFile file = ((FileContent)content).getFile(); + + Document document = FileDocumentManager.getInstance().getCachedDocument(file); + if (document != null) { + FileDocumentManager.getInstance().saveDocument(document); + } + + if (file.isInLocalFileSystem()) { + return new LocalOutputFile(file); } String tempFileName = getFileName(null, windowTitle, content.getContentType()); - File tempFile = createTempFile(fileContent.getFile(), tempFileName); - return new NonLocalOutputFile(fileContent.getFile(), tempFile); + File tempFile = createTempFile(file, tempFileName); + return new NonLocalOutputFile(file, tempFile); } else if (content instanceof DocumentContent) { String tempFileName = getFileName(null, windowTitle, content.getContentType()); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java index dae717310766..d54b5158f70f 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java @@ -191,6 +191,9 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { group.add(new MyReadOnlyLockAction()); group.add(myEditorSettingsAction); + group.add(Separator.getInstance()); + group.addAll(super.createToolbarActions()); + return group; } @@ -206,6 +209,9 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { group.add(Separator.getInstance()); group.add(new MyToggleExpandByDefaultAction()); + group.add(Separator.getInstance()); + group.addAll(super.createPopupActions()); + return group; } @@ -219,8 +225,8 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { group.add(new AppendSelectedChangesAction(Side.RIGHT, false)); group.add(new RevertSelectedChangesAction(Side.LEFT)); group.add(new RevertSelectedChangesAction(Side.RIGHT)); - group.add(Separator.getInstance()); + group.add(Separator.getInstance()); group.addAll(TextDiffViewerUtil.createEditorPopupActions()); return group; diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java index a2d0a598e953..d33009779ff7 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java @@ -115,10 +115,13 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { group.add(new MyReadOnlyLockAction()); group.add(myEditorSettingsAction); + group.add(Separator.getInstance()); + group.addAll(super.createToolbarActions()); + return group; } - @Nullable + @NotNull @Override protected List createPopupActions() { List group = new ArrayList(); @@ -131,6 +134,9 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { group.add(new MyToggleAutoScrollAction()); group.add(new MyToggleExpandByDefaultAction()); + group.add(Separator.getInstance()); + group.addAll(super.createPopupActions()); + return group; } @@ -145,8 +151,8 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { group.add(new AppendSelectedChangesAction(Side.RIGHT, false)); group.add(new RevertSelectedChangesAction(Side.LEFT)); group.add(new RevertSelectedChangesAction(Side.RIGHT)); - group.add(Separator.getInstance()); + group.add(Separator.getInstance()); group.addAll(super.createEditorPopupActions()); return group; diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java index ad630c48639b..9f76fe97368a 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java @@ -80,10 +80,13 @@ public class SimpleOnesideDiffViewer extends OnesideTextDiffViewer { group.add(new MyReadOnlyLockAction()); group.add(myEditorSettingsAction); + group.add(Separator.getInstance()); + group.addAll(super.createToolbarActions()); + return group; } - @Nullable + @NotNull @Override protected List createPopupActions() { List group = new ArrayList(); @@ -93,6 +96,9 @@ public class SimpleOnesideDiffViewer extends OnesideTextDiffViewer { group.add(Separator.getInstance()); group.add(new MyHighlightPolicySettingAction().getPopupGroup()); + group.add(Separator.getInstance()); + group.addAll(super.createPopupActions()); + return group; } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java index adc2a32c1489..bf76d6ca144d 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java @@ -78,10 +78,13 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { group.add(new ShowBaseRightPartialDiffAction()); group.add(new ShowLeftRightPartialDiffAction()); + group.add(Separator.getInstance()); + group.addAll(super.createToolbarActions()); + return group; } - @Nullable + @NotNull @Override protected List createPopupActions() { List group = new ArrayList(); @@ -94,6 +97,9 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { group.add(new MyToggleAutoScrollAction()); group.add(new MyToggleExpandByDefaultAction()); + group.add(Separator.getInstance()); + group.addAll(super.createPopupActions()); + return group; } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java b/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java index 657e0d3a5561..a17deb7f07df 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java @@ -21,9 +21,7 @@ import com.intellij.diff.FrameDiffTool.DiffViewer; import com.intellij.diff.requests.ContentDiffRequest; import com.intellij.diff.tools.util.DiffDataKeys; import com.intellij.diff.util.DiffTaskQueue; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.OpenFileDescriptor; @@ -33,10 +31,12 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.util.Alarm; import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.*; import javax.swing.*; +import java.util.ArrayList; import java.util.List; public abstract class DiffViewerBase implements DiffViewer, DataProvider { @@ -186,14 +186,16 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { return myContext.isWindowFocused(); } - @Nullable protected List createToolbarActions() { - return null; + List group = new ArrayList(); + ContainerUtil.addAll(group, ((ActionGroup)ActionManager.getInstance().getAction(IdeActions.DIFF_VIEWER_TOOLBAR)).getChildren(null)); + return group; } - @Nullable protected List createPopupActions() { - return null; + List group = new ArrayList(); + ContainerUtil.addAll(group, ((ActionGroup)ActionManager.getInstance().getAction(IdeActions.DIFF_VIEWER_POPUP)).getChildren(null)); + return group; } @Nullable diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java index 8a7b65eb2ce1..860a59f1a3f0 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java @@ -834,10 +834,6 @@ public class DiffUtil { boolean isUnderDialog = LaterInvocator.isInModalContext(); mode = isUnderDialog ? WindowWrapper.Mode.MODAL : WindowWrapper.Mode.FRAME; } - if (mode == WindowWrapper.Mode.FRAME && - (hints.getOkAction() != null || hints.getCancelAction() != null)) { - mode = WindowWrapper.Mode.MODAL; - } return mode; } diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/ColorBlindness.java b/platform/editor-ui-api/src/com/intellij/ide/ui/ColorBlindness.java index bf8ac42bcd9b..77227715b848 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/ColorBlindness.java +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/ColorBlindness.java @@ -31,7 +31,7 @@ public enum ColorBlindness { *
  • mid-greens with some oranges.
  • * */ - protanopia, + protanopia("color.blindness.protanopia.name"), /** * Lacking the medium-wavelength sensitive retinal cones, * those with this condition are unable to distinguish @@ -46,7 +46,7 @@ public enum ColorBlindness { *
  • light blues with lilac.
  • * */ - deuteranopia, + deuteranopia("color.blindness.deuteranopia.name"), /** * Lacking the short-wavelength sensitive retinal cones, * those affected see short-wavelength colors (blue, indigo and a spectral violet) @@ -59,9 +59,15 @@ public enum ColorBlindness { *
  • oranges with reds.
  • * */ - tritanopia, + tritanopia("color.blindness.tritanopia.name"), /** * Total color blindness is defined as the inability to see color. */ - achromatopsia + achromatopsia("color.blindness.achromatopsia.name"); + + public final String key; + + ColorBlindness(String key) { + this.key = key; + } } diff --git a/platform/icons/src/providers/sqlServer.png b/platform/icons/src/providers/sqlServer.png new file mode 100644 index 000000000000..a7a613ae9dff Binary files /dev/null and b/platform/icons/src/providers/sqlServer.png differ diff --git a/platform/icons/src/providers/sqlServer@2x.png b/platform/icons/src/providers/sqlServer@2x.png new file mode 100644 index 000000000000..bfaa483a8263 Binary files /dev/null and b/platform/icons/src/providers/sqlServer@2x.png differ diff --git a/platform/icons/src/providers/sqlServer@2x_dark.png b/platform/icons/src/providers/sqlServer@2x_dark.png new file mode 100644 index 000000000000..2e51408786e8 Binary files /dev/null and b/platform/icons/src/providers/sqlServer@2x_dark.png differ diff --git a/platform/icons/src/providers/sqlServer_dark.png b/platform/icons/src/providers/sqlServer_dark.png new file mode 100644 index 000000000000..a665f04b1555 Binary files /dev/null and b/platform/icons/src/providers/sqlServer_dark.png differ diff --git a/platform/testRunner/src/com/intellij/execution/testframework/PoolOfTestIcons.java b/platform/lang-api/src/com/intellij/execution/testframework/PoolOfTestIcons.java similarity index 100% rename from platform/testRunner/src/com/intellij/execution/testframework/PoolOfTestIcons.java rename to platform/lang-api/src/com/intellij/execution/testframework/PoolOfTestIcons.java diff --git a/platform/lang-api/src/com/intellij/execution/testframework/TestIconMapper.java b/platform/lang-api/src/com/intellij/execution/testframework/TestIconMapper.java new file mode 100644 index 000000000000..f7573aae0e36 --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/testframework/TestIconMapper.java @@ -0,0 +1,66 @@ +/* + * 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.execution.testframework; + +import com.intellij.execution.testframework.sm.runner.states.TestStateInfo; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Dmitry Avdeev + */ +public class TestIconMapper implements PoolOfTestIcons { + + private final static Map magnitudes = new HashMap(); + + static { + for (TestStateInfo.Magnitude value : TestStateInfo.Magnitude.values()) { + magnitudes.put(value.getValue(), value); + } + } + + public static TestStateInfo.Magnitude getMagnitude(int value) { + return magnitudes.get(value); + } + + @Nullable + public static Icon getIcon(TestStateInfo.Magnitude magnitude) { + switch (magnitude) { + case SKIPPED_INDEX: + return SKIPPED_ICON; + case COMPLETE_INDEX: + return PASSED_ICON; + case NOT_RUN_INDEX: + return NOT_RAN; + case RUNNING_INDEX: + return null; + case TERMINATED_INDEX: + return TERMINATED_ICON; + case IGNORED_INDEX: + return IGNORED_ICON; + case FAILED_INDEX: + return FAILED_ICON; + case ERROR_INDEX: + return ERROR_ICON; + case PASSED_INDEX: + return PASSED_ICON; + } + return null; + } +} diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestStateInfo.java b/platform/lang-api/src/com/intellij/execution/testframework/sm/runner/states/TestStateInfo.java similarity index 67% rename from platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestStateInfo.java rename to platform/lang-api/src/com/intellij/execution/testframework/sm/runner/states/TestStateInfo.java index e3c58ff1bcfe..f476ea2e5f61 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestStateInfo.java +++ b/platform/lang-api/src/com/intellij/execution/testframework/sm/runner/states/TestStateInfo.java @@ -15,7 +15,7 @@ */ package com.intellij.execution.testframework.sm.runner.states; -import com.intellij.execution.testframework.sm.SMTestsRunnerBundle; +import com.intellij.execution.ExecutionBundle; /** * @author Roman Chernyatchik @@ -32,7 +32,6 @@ public interface TestStateInfo { * Some magic definition from AbstractTestProxy class. * If state is defect something wrong is with it and should be shown * properly in UI. - * @return */ boolean isDefect(); @@ -54,22 +53,21 @@ public interface TestStateInfo { boolean wasTerminated(); /** - * It's some magic parameter than describe state type. - * @return + * It's some magic parameter that describes state type. */ Magnitude getMagnitude(); //WARN: It is Hack, see PoolOfTestStates, API is necessary enum Magnitude { - SKIPPED_INDEX(0, 1, SMTestsRunnerBundle.message("sm.test.runner.magnitude.skipped.failed.title")), - COMPLETE_INDEX(1, 3, SMTestsRunnerBundle.message("sm.test.runner.magnitude.completed.failed.title")), - NOT_RUN_INDEX(2, 0, SMTestsRunnerBundle.message("sm.test.runner.magnitude.not.run.failed.title")), - RUNNING_INDEX(3, 7, SMTestsRunnerBundle.message("sm.test.runner.magnitude.running.failed.title")), - TERMINATED_INDEX(4, 6, SMTestsRunnerBundle.message("sm.test.runner.magnitude.terminated.failed.title")), - IGNORED_INDEX(5, 2, SMTestsRunnerBundle.message("sm.test.runner.magnitude.ignored.failed.title")), - FAILED_INDEX(6, 4, SMTestsRunnerBundle.message("sm.test.runner.magnitude.assertion.failed.title")), - ERROR_INDEX(8, 5, SMTestsRunnerBundle.message("sm.test.runner.magnitude.testerror.title")), - PASSED_INDEX(COMPLETE_INDEX.getValue(), COMPLETE_INDEX.getSortWeight(), SMTestsRunnerBundle.message("sm.test.runner.magnitude.passed.title")); + SKIPPED_INDEX(0, 1, ExecutionBundle.message("sm.test.runner.magnitude.skipped.failed.title")), + COMPLETE_INDEX(1, 3, ExecutionBundle.message("sm.test.runner.magnitude.completed.failed.title")), + NOT_RUN_INDEX(2, 0, ExecutionBundle.message("sm.test.runner.magnitude.not.run.failed.title")), + RUNNING_INDEX(3, 7, ExecutionBundle.message("sm.test.runner.magnitude.running.failed.title")), + TERMINATED_INDEX(4, 6, ExecutionBundle.message("sm.test.runner.magnitude.terminated.failed.title")), + IGNORED_INDEX(5, 2, ExecutionBundle.message("sm.test.runner.magnitude.ignored.failed.title")), + FAILED_INDEX(6, 4, ExecutionBundle.message("sm.test.runner.magnitude.assertion.failed.title")), + ERROR_INDEX(8, 5, ExecutionBundle.message("sm.test.runner.magnitude.testerror.title")), + PASSED_INDEX(COMPLETE_INDEX.getValue(), COMPLETE_INDEX.getSortWeight(), ExecutionBundle.message("sm.test.runner.magnitude.passed.title")); private final int myValue; private final int mySortWeight; diff --git a/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoUtils.java b/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoUtils.java index 92b6e2f55c1e..f8ed8d2ad4ed 100644 --- a/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoUtils.java +++ b/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoUtils.java @@ -17,10 +17,12 @@ package com.intellij.lang.parameterInfo; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.SyntaxTraverser; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.text.CharArrayUtil; @@ -53,16 +55,21 @@ public class ParameterInfoUtils { } public static int getCurrentParameterIndex(ASTNode argList, int offset, IElementType delimiterType) { - int curOffset = argList.getTextRange().getStartOffset(); + SyntaxTraverser s = SyntaxTraverser.astTraverser(argList).expandAndSkip(Conditions.is(argList)); + return getCurrentParameterIndex(s, offset, delimiterType); + } + + public static int getCurrentParameterIndex(SyntaxTraverser s, int offset, IElementType delimiterType) { + V root = s.getRoot(); + int curOffset = s.api.rangeOf(root).getStartOffset(); if (offset < curOffset) return -1; - ASTNode[] children = argList.getChildren(null); int index = 0; - for (ASTNode child : children) { - curOffset += child.getTextLength(); + for (V child : s) { + curOffset += s.api.rangeOf(child).getLength(); if (offset < curOffset) break; - IElementType type = child.getElementType(); + IElementType type = s.api.typeOf(child); if (type == delimiterType) index++; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/TemplateInsertHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/TemplateInsertHandler.java deleted file mode 100644 index 098f8ce69c2b..000000000000 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/TemplateInsertHandler.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.codeInsight.completion; - -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupItem; -import com.intellij.codeInsight.template.Template; -import com.intellij.codeInsight.template.TemplateEditingAdapter; -import com.intellij.codeInsight.template.TemplateManager; -import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.RangeMarker; -import com.intellij.openapi.util.Key; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import org.jetbrains.annotations.NotNull; - -/** - * @author spleaner - */ -public abstract class TemplateInsertHandler implements InsertHandler { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.TemplateInsertHandler"); - - protected static final Object EXPANDED_TEMPLATE_ATTR = Key.create("EXPANDED_TEMPLATE_ATTR"); - - @Override - public void handleInsert(final InsertionContext context, final LookupElement item) { - if (isTemplateToBeCompleted(item)) { - context.setAddCompletionChar(false); - handleTemplate((LookupItem) item, context); - } - } - - protected static boolean isTemplateToBeCompleted(final LookupElement lookupItem) { - return lookupItem instanceof LookupItem && lookupItem.getObject() instanceof Template - && ((LookupItem)lookupItem).getAttribute(EXPANDED_TEMPLATE_ATTR) == null; - } - - protected void handleTemplate(@NotNull final LookupItem lookupItem, - @NotNull final InsertionContext context) { - LOG.assertTrue(CommandProcessor.getInstance().getCurrentCommand() != null); - PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getEditor().getDocument()); - - Template template = (Template)lookupItem.getObject(); - - final Editor editor = context.getEditor(); - final Document document = editor.getDocument(); - - final int templateStartOffset = context.getStartOffset(); - document.replaceString(templateStartOffset, templateStartOffset + lookupItem.getLookupString().length(), ""); - - final RangeMarker offsetRangeMarker = document.createRangeMarker(templateStartOffset, templateStartOffset); - - TemplateManager.getInstance(editor.getProject()).startTemplate(editor, template, new TemplateEditingAdapter() { - @Override - public void templateFinished(Template template, boolean brokenOff) { - lookupItem.setAttribute(EXPANDED_TEMPLATE_ATTR, Boolean.TRUE); - - if (!offsetRangeMarker.isValid()) return; - - final Editor editor = context.getEditor(); - final int startOffset = offsetRangeMarker.getStartOffset(); - final int endOffset = editor.getCaretModel().getOffset(); - String lookupString = editor.getDocument().getCharsSequence().subSequence(startOffset, endOffset).toString(); - lookupItem.setLookupString(lookupString); - - final OffsetMap offsetMap = new OffsetMap(document); - offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, startOffset); - offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, endOffset); - offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, endOffset); - - final PsiFile psiFile = context.getFile(); - - InsertionContext newContext = - new InsertionContext(offsetMap, context.getCompletionChar(), LookupElement.EMPTY_ARRAY, psiFile, editor, context.shouldAddCompletionChar()); - - populateInsertMap(psiFile, offsetMap); - - handleInsert(newContext, lookupItem); - } - }); - } - - protected void populateInsertMap(@NotNull final PsiFile file, @NotNull final OffsetMap offsetMap) { - } - -} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableActionHandlerBasedAction.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableActionHandlerBasedAction.java index d24f34839977..f083860d8e24 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableActionHandlerBasedAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableActionHandlerBasedAction.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.generation.actions; +import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.actions.BaseCodeInsightAction; import com.intellij.lang.ContextAwareActionHandler; import com.intellij.lang.Language; @@ -56,14 +57,14 @@ public abstract class PresentableActionHandlerBasedAction extends BaseCodeInsigh @NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext, @Nullable String actionPlace) { // avoid evaluating isValidFor several times unnecessary - LanguageCodeInsightActionHandler handler = getValidHandler(editor, file); + CodeInsightActionHandler handler = getValidHandler(editor, file); presentation.setEnabled(handler != null); if (handler instanceof ContextAwareActionHandler && !ActionPlaces.isMainMenuOrActionSearch(actionPlace)) { presentation.setVisible(((ContextAwareActionHandler)handler).isAvailableForQuickList(editor, file, dataContext)); } - if (presentation.isVisible() && handler instanceof PresentableLanguageCodeInsightActionHandler) { - ((PresentableLanguageCodeInsightActionHandler)handler).update(editor, file, presentation); + if (presentation.isVisible() && handler instanceof PresentableCodeInsightActionHandler) { + ((PresentableCodeInsightActionHandler)handler).update(editor, file, presentation); } } @@ -73,13 +74,22 @@ public abstract class PresentableActionHandlerBasedAction extends BaseCodeInsigh } @Nullable - private LanguageCodeInsightActionHandler getValidHandler(@NotNull Editor editor, @NotNull PsiFile file) { + private CodeInsightActionHandler getValidHandler(@NotNull Editor editor, @NotNull PsiFile file) { Language language = PsiUtilCore.getLanguageAtOffset(file, editor.getCaretModel().getOffset()); - final LanguageCodeInsightActionHandler codeInsightActionHandler = getLanguageExtension().forLanguage(language); - if (codeInsightActionHandler != null && codeInsightActionHandler.isValidFor(editor, file)) return codeInsightActionHandler; + final CodeInsightActionHandler codeInsightActionHandler = getLanguageExtension().forLanguage(language); + if (codeInsightActionHandler != null) { + if (codeInsightActionHandler instanceof LanguageCodeInsightActionHandler) { + if (((LanguageCodeInsightActionHandler)codeInsightActionHandler).isValidFor(editor, file)) { + return codeInsightActionHandler; + } + } + else { + return codeInsightActionHandler; + } + } return null; } @NotNull - protected abstract LanguageExtension getLanguageExtension(); + protected abstract LanguageExtension getLanguageExtension(); } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableCodeInsightActionHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableCodeInsightActionHandler.java new file mode 100644 index 000000000000..c9a3d8fd8ac1 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableCodeInsightActionHandler.java @@ -0,0 +1,27 @@ +/* + * 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.codeInsight.generation.actions; + +import com.intellij.codeInsight.CodeInsightActionHandler; +import com.intellij.lang.LanguageCodeInsightActionHandler; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; + +public interface PresentableCodeInsightActionHandler extends CodeInsightActionHandler { + void update(@NotNull Editor editor, @NotNull PsiFile file, Presentation presentation); +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableLanguageCodeInsightActionHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableLanguageCodeInsightActionHandler.java index ecf3ff409579..6eac1b8b056b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableLanguageCodeInsightActionHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/PresentableLanguageCodeInsightActionHandler.java @@ -21,6 +21,5 @@ import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; -public interface PresentableLanguageCodeInsightActionHandler extends LanguageCodeInsightActionHandler { - void update(@NotNull Editor editor, @NotNull PsiFile file, Presentation presentation); +public interface PresentableLanguageCodeInsightActionHandler extends PresentableCodeInsightActionHandler, LanguageCodeInsightActionHandler { } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/LookupItem.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/LookupItem.java index 9b846da394f5..50a7de4f16cf 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/LookupItem.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/LookupItem.java @@ -53,7 +53,6 @@ public class LookupItem extends MutableLookupElement implements Comparable public static final Object FORCE_QUALIFY = Key.create("FORCE_QUALIFY"); public static final Object SUBSTITUTOR = Key.create("SUBSTITUTOR"); - public static final Object TYPE = Key.create("TYPE"); public static final Object CASE_INSENSITIVE = Key.create("CASE_INSENSITIVE"); @@ -77,6 +76,9 @@ public class LookupItem extends MutableLookupElement implements Comparable setLookupString(lookupString); } + /** + * @deprecated use {@link LookupElementBuilder} + */ public static LookupItem fromString(String s) { return new LookupItem(s, s); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoSuperAction.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoSuperAction.java index 6439d4fc3833..fd9db43d8ed1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoSuperAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoSuperAction.java @@ -17,9 +17,10 @@ package com.intellij.codeInsight.navigation.actions; import com.intellij.codeInsight.CodeInsightActionHandler; -import com.intellij.codeInsight.actions.BaseCodeInsightAction; +import com.intellij.codeInsight.generation.actions.PresentableActionHandlerBasedAction; import com.intellij.lang.CodeInsightActions; import com.intellij.lang.Language; +import com.intellij.lang.LanguageExtension; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.DumbAware; @@ -31,7 +32,7 @@ import com.intellij.psi.util.PsiUtilCore; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -public class GotoSuperAction extends BaseCodeInsightAction implements CodeInsightActionHandler, DumbAware { +public class GotoSuperAction extends PresentableActionHandlerBasedAction implements CodeInsightActionHandler, DumbAware { @NonNls public static final String FEATURE_ID = "navigation.goto.super"; @@ -74,4 +75,10 @@ public class GotoSuperAction extends BaseCodeInsightAction implements CodeInsigh event.getPresentation().setVisible(false); } } + + @NotNull + @Override + protected LanguageExtension getLanguageExtension() { + return CodeInsightActions.GOTO_SUPER; + } } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java b/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java index b6a68ef88fb0..3d16648f29c5 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java @@ -24,16 +24,17 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.*; import com.intellij.codeInspection.ex.InspectionToolWrapper; import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; -import com.intellij.openapi.application.ApplicationManager; +import com.intellij.codeInspection.ex.PerformFixesModalTask; +import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; -import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.SequentialModalProgressTask; import org.jetbrains.annotations.NotNull; import java.util.Collections; @@ -87,30 +88,22 @@ public class CleanupInspectionIntention implements IntentionAction, HighPriority return d2.getTextRange().getStartOffset() - d1.getTextRange().getStartOffset(); } }); - boolean applicableFixFound = false; - for (final ProblemDescriptor descriptor : descriptions) { - final QuickFix[] fixes = descriptor.getFixes(); - if (fixes != null && fixes.length > 0) { - for (final QuickFix fix : fixes) { - if (fix != null && fix.getClass().isAssignableFrom(myQuickfixClass)) { - final PsiElement element = descriptor.getPsiElement(); - if (element != null && element.isValid()) { - applicableFixFound = true; - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - fix.applyFix(project, descriptor); - } - }); - PsiDocumentManager.getInstance(project).commitAllDocuments(); - } - break; - } - } + + final String templatePresentationText = "Apply Fixes"; + final SequentialModalProgressTask progressTask = + new SequentialModalProgressTask(project, templatePresentationText, false); + final PerformFixesTask fixesTask = new PerformFixesTask(project, descriptions.toArray(new CommonProblemDescriptor[descriptions.size()]), progressTask); + CommandProcessor.getInstance().executeCommand(project, new Runnable() { + @Override + public void run() { + CommandProcessor.getInstance().markCurrentCommandAsGlobal(project); + progressTask.setMinIterationTime(200); + progressTask.setTask(fixesTask); + ProgressManager.getInstance().run(progressTask); } - } + }, templatePresentationText, null); - if (!applicableFixFound) { + if (!fixesTask.isApplicableFixFound()) { HintManager.getInstance().showErrorHint(editor, "Unfortunately '" + myText + "' is currently not available for batch mode"); } } @@ -125,4 +118,35 @@ public class CleanupInspectionIntention implements IntentionAction, HighPriority public boolean startInWriteAction() { return false; } + + private class PerformFixesTask extends PerformFixesModalTask { + private boolean myApplicableFixFound = false; + + public PerformFixesTask(@NotNull Project project, + @NotNull CommonProblemDescriptor[] descriptors, + @NotNull SequentialModalProgressTask task) { + super(project, descriptors, task); + } + + @Override + protected void applyFix(Project project, CommonProblemDescriptor descriptor) { + final QuickFix[] fixes = descriptor.getFixes(); + if (fixes != null && fixes.length > 0) { + for (final QuickFix fix : fixes) { + if (fix != null && fix.getClass().isAssignableFrom(myQuickfixClass)) { + final PsiElement element = ((ProblemDescriptor)descriptor).getPsiElement(); + if (element != null && element.isValid()) { + fix.applyFix(project, descriptor); + myApplicableFixFound = true; + } + break; + } + } + } + } + + public boolean isApplicableFixFound() { + return myApplicableFixFound; + } + } } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileManagerImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileManagerImpl.java index c2bb68500ef1..377146963f6a 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileManagerImpl.java @@ -38,8 +38,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.WriteExternalException; import com.intellij.profile.Profile; import com.intellij.profile.codeInspection.InspectionProfileLoadUtil; import com.intellij.profile.codeInspection.InspectionProfileManager; @@ -71,7 +69,7 @@ import java.util.concurrent.atomic.AtomicBoolean; public class InspectionProfileManagerImpl extends InspectionProfileManager implements SeverityProvider, PersistentStateComponent { private final InspectionToolRegistrar myRegistrar; - private final SchemesManager mySchemesManager; + private final SchemesManager mySchemeManager; private final AtomicBoolean myProfilesAreInitialized = new AtomicBoolean(false); private final SeverityRegistrar mySeverityRegistrar; @@ -85,7 +83,7 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple myRegistrar = registrar; registerProvidedSeverities(); - mySchemesManager = schemesManagerFactory.create(INSPECTION_DIR, new BaseSchemeProcessor() { + mySchemeManager = schemesManagerFactory.create(INSPECTION_DIR, new BaseSchemeProcessor() { @NotNull @Override public InspectionProfileImpl readScheme(@NotNull Element element) { @@ -133,7 +131,7 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple @Override public void onCurrentSchemeChanged(@Nullable Scheme oldScheme) { - Profile current = mySchemesManager.getCurrentScheme(); + Profile current = mySchemeManager.getCurrentScheme(); if (current != null) { fireProfileChanged((Profile)oldScheme, current, null); } @@ -164,7 +162,7 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple @NotNull public Collection getProfiles() { initProfiles(); - return mySchemesManager.getAllSchemes(); + return mySchemeManager.getAllSchemes(); } private volatile boolean LOAD_PROFILES = !ApplicationManager.getApplication().isUnitTestMode(); @@ -177,15 +175,15 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple @Override public void initProfiles() { if (myProfilesAreInitialized.getAndSet(true)) { - if (mySchemesManager.getAllSchemes().isEmpty()) { + if (mySchemeManager.getAllSchemes().isEmpty()) { createDefaultProfile(); } return; } if (!LOAD_PROFILES) return; - mySchemesManager.loadSchemes(); - Collection profiles = mySchemesManager.getAllSchemes(); + mySchemeManager.loadSchemes(); + Collection profiles = mySchemeManager.getAllSchemes(); if (profiles.isEmpty()) { createDefaultProfile(); } @@ -230,7 +228,7 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple @Override public void updateProfile(@NotNull Profile profile) { - mySchemesManager.addScheme(profile); + mySchemeManager.addScheme(profile); updateProfileImpl(profile); } @@ -256,23 +254,13 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple @Override public Element getState() { Element state = new Element("state"); - try { - mySeverityRegistrar.writeExternal(state); - } - catch (WriteExternalException e) { - throw new RuntimeException(e); - } + mySeverityRegistrar.writeExternal(state); return state; } @Override public void loadState(Element state) { - try { - mySeverityRegistrar.readExternal(state); - } - catch (InvalidDataException e) { - throw new RuntimeException(e); - } + mySeverityRegistrar.readExternal(state); } public InspectionProfileConvertor getConverter() { @@ -286,20 +274,20 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple @Override public void setRootProfile(String rootProfile) { - Profile current = mySchemesManager.getCurrentScheme(); + Profile current = mySchemeManager.getCurrentScheme(); if (current == null || !Comparing.strEqual(rootProfile, current.getName())) { Profile scheme = getProfile(rootProfile); if (scheme == null && current == null) { return; } fireProfileChanged(current, scheme, null); - mySchemesManager.setCurrent(scheme, false); + mySchemeManager.setCurrent(scheme, false); } } @Override public Profile getProfile(@NotNull final String name, boolean returnRootProfileIfNamedIsAbsent) { - Profile found = mySchemesManager.findSchemeByName(name); + Profile found = mySchemeManager.findSchemeByName(name); if (found != null) return found; //profile was deleted if (returnRootProfileIfNamedIsAbsent) { @@ -311,7 +299,7 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple @NotNull @Override public Profile getRootProfile() { - Profile current = mySchemesManager.getCurrentScheme(); + Profile current = mySchemeManager.getCurrentScheme(); if (current != null) return current; Collection profiles = getProfiles(); if (profiles.isEmpty()) return createSampleProfile(InspectionProfileImpl.DEFAULT_PROFILE_NAME, null); @@ -320,21 +308,21 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple @Override public void deleteProfile(@NotNull final String profile) { - Profile found = mySchemesManager.findSchemeByName(profile); + Profile found = mySchemeManager.findSchemeByName(profile); if (found != null) { - mySchemesManager.removeScheme(found); + mySchemeManager.removeScheme(found); } } @Override public void addProfile(@NotNull final Profile profile) { - mySchemesManager.addScheme(profile); + mySchemeManager.addScheme(profile); } @Override @NotNull public String[] getAvailableProfileNames() { - return ArrayUtil.toStringArray(mySchemesManager.getAllSchemeNames()); + return ArrayUtil.toStringArray(mySchemeManager.getAllSchemeNames()); } @Override diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/PerformFixesModalTask.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/PerformFixesModalTask.java new file mode 100644 index 000000000000..100110413b68 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/PerformFixesModalTask.java @@ -0,0 +1,85 @@ +/* + * 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.codeInspection.ex; + +import com.intellij.codeInspection.CommonProblemDescriptor; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.presentation.java.SymbolPresentationUtil; +import com.intellij.util.SequentialModalProgressTask; +import com.intellij.util.SequentialTask; +import org.jetbrains.annotations.NotNull; + +public abstract class PerformFixesModalTask implements SequentialTask { + @NotNull + protected final Project myProject; + private final CommonProblemDescriptor[] myDescriptors; + private final SequentialModalProgressTask myTask; + private final PsiDocumentManager myDocumentManager; + private int myCount = 0; + + public PerformFixesModalTask(@NotNull Project project, + @NotNull CommonProblemDescriptor[] descriptors, + @NotNull SequentialModalProgressTask task) { + myProject = project; + myDescriptors = descriptors; + myTask = task; + myDocumentManager = PsiDocumentManager.getInstance(myProject); + } + + @Override + public void prepare() { + } + + @Override + public boolean isDone() { + return myCount > myDescriptors.length - 1; + } + + @Override + public boolean iteration() { + final CommonProblemDescriptor descriptor = myDescriptors[myCount++]; + ProgressIndicator indicator = myTask.getIndicator(); + if (indicator != null) { + indicator.setFraction((double)myCount / myDescriptors.length); + String presentableText = "usages"; + if (descriptor instanceof ProblemDescriptor) { + final PsiElement psiElement = ((ProblemDescriptor)descriptor).getPsiElement(); + if (psiElement != null) { + presentableText = SymbolPresentationUtil.getSymbolPresentableText(psiElement); + } + } + indicator.setText("Processing " + presentableText); + } + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + myDocumentManager.commitAllDocuments(); + applyFix(myProject, descriptor); + } + }); + return isDone(); + } + + @Override + public void stop() {} + + protected abstract void applyFix(Project project, CommonProblemDescriptor descriptor); +} diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java index 2be41efe9399..52d0133ee364 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java @@ -31,7 +31,6 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CustomShortcutSet; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.ReadonlyStatusHandler; @@ -40,9 +39,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.presentation.java.SymbolPresentationUtil; import com.intellij.util.SequentialModalProgressTask; -import com.intellij.util.SequentialTask; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -148,22 +145,19 @@ public class QuickFixAction extends AnAction { try { final Set ignoredElements = new HashSet(); + final String templatePresentationText = getTemplatePresentation().getText(); + assert templatePresentationText != null; CommandProcessor.getInstance().executeCommand(project, new Runnable() { @Override public void run() { CommandProcessor.getInstance().markCurrentCommandAsGlobal(project); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - final SequentialModalProgressTask progressTask = - new SequentialModalProgressTask(project, getTemplatePresentation().getText(), false); - progressTask.setMinIterationTime(200); - progressTask.setTask(new PerformFixesTask(project, descriptors, ignoredElements, progressTask, context)); - ProgressManager.getInstance().run(progressTask); - } - }); + final SequentialModalProgressTask progressTask = + new SequentialModalProgressTask(project, templatePresentationText, false); + progressTask.setMinIterationTime(200); + progressTask.setTask(new PerformFixesTask(project, descriptors, ignoredElements, progressTask, context)); + ProgressManager.getInstance().run(progressTask); } - }, getTemplatePresentation().getText(), null); + }, templatePresentationText, null); refreshViews(project, ignoredElements, myToolWrapper); } @@ -300,52 +294,24 @@ public class QuickFixAction extends AnAction { return true; } - private class PerformFixesTask implements SequentialTask { - @NotNull - private final Project myProject; - private final CommonProblemDescriptor[] myDescriptors; + private class PerformFixesTask extends PerformFixesModalTask { + @NotNull private final GlobalInspectionContextImpl myContext; @NotNull private final Set myIgnoredElements; - private final SequentialModalProgressTask myTask; - @NotNull private final GlobalInspectionContextImpl myContext; - private int myCount = 0; public PerformFixesTask(@NotNull Project project, @NotNull CommonProblemDescriptor[] descriptors, @NotNull Set ignoredElements, @NotNull SequentialModalProgressTask task, @NotNull GlobalInspectionContextImpl context) { - myProject = project; - myDescriptors = descriptors; - myIgnoredElements = ignoredElements; - myTask = task; + super(project, descriptors, task); myContext = context; + myIgnoredElements = ignoredElements; } @Override - public void prepare() { - } - - @Override - public boolean isDone() { - return myCount > myDescriptors.length - 1; - } - - @Override - public boolean iteration() { - final CommonProblemDescriptor descriptor = myDescriptors[myCount++]; - ProgressIndicator indicator = myTask.getIndicator(); - if (indicator != null) { - indicator.setFraction((double)myCount / myDescriptors.length); - if (descriptor instanceof ProblemDescriptor) { - final PsiElement psiElement = ((ProblemDescriptor)descriptor).getPsiElement(); - if (psiElement != null) { - indicator.setText("Processing " + SymbolPresentationUtil.getSymbolPresentableText(psiElement)); - } - } - } - applyFix(myProject, myContext, new CommonProblemDescriptor[]{descriptor}, myIgnoredElements); - return isDone(); + protected void applyFix(Project project, CommonProblemDescriptor descriptor) { + QuickFixAction.this.applyFix(myProject, myContext, new CommonProblemDescriptor[]{descriptor}, myIgnoredElements); } @Override diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java index b90bc6f023b7..a356af9e1ced 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java @@ -720,12 +720,7 @@ public class RunManagerImpl extends RunManagerEx implements PersistentStateCompo } } - try { - myOrder.readExternal(parentNode); - } - catch (InvalidDataException e) { - throw new RuntimeException(e); - } + myOrder.readExternal(parentNode); // migration (old ids to UUIDs) readList(myOrder); @@ -734,12 +729,7 @@ public class RunManagerImpl extends RunManagerEx implements PersistentStateCompo Element recentNode = parentNode.getChild(RECENT); if (recentNode != null) { JDOMExternalizableStringList list = new JDOMExternalizableStringList(); - try { - list.readExternal(recentNode); - } - catch (InvalidDataException e) { - throw new RuntimeException(e); - } + list.readExternal(recentNode); readList(list); for (String name : list) { RunnerAndConfigurationSettings settings = myConfigurations.get(name); diff --git a/platform/lang-impl/src/com/intellij/execution/lineMarker/RunLineMarkerProvider.java b/platform/lang-impl/src/com/intellij/execution/lineMarker/RunLineMarkerProvider.java index 959094599181..5b4a70dbebd4 100644 --- a/platform/lang-impl/src/com/intellij/execution/lineMarker/RunLineMarkerProvider.java +++ b/platform/lang-impl/src/com/intellij/execution/lineMarker/RunLineMarkerProvider.java @@ -57,7 +57,7 @@ public class RunLineMarkerProvider implements LineMarkerProvider { } if (icon == null) return null; - return new LineMarkerInfo(element, element.getTextOffset(), icon, Pass.UPDATE_ALL, null, null) { + return new LineMarkerInfo(element, element.getTextOffset(), icon, Pass.UPDATE_ALL, null, null, GutterIconRenderer.Alignment.CENTER) { @Nullable @Override public GutterIconRenderer createGutterRenderer() { diff --git a/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java b/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java index 87d0b78acf39..a8ced50fb15c 100644 --- a/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java +++ b/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java @@ -30,7 +30,6 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Factory; -import com.intellij.psi.PsiDirectory; import com.intellij.ui.content.Content; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewManager; @@ -96,8 +95,7 @@ public class FindInProjectManager { } public void startFindInProject(@NotNull FindModel findModel) { - final PsiDirectory psiDirectory = FindInProjectUtil.getPsiDirectory(findModel, myProject); - if (findModel.getDirectoryName() != null && psiDirectory == null){ + if (findModel.getDirectoryName() != null && FindInProjectUtil.getDirectory(findModel) == null) { return; } @@ -133,7 +131,7 @@ public class FindInProjectManager { return processor.process(usage); } }; - FindInProjectUtil.findUsages(findModelCopy, psiDirectory, myProject, consumer, processPresentation); + FindInProjectUtil.findUsages(findModelCopy, myProject, consumer, processPresentation); } finally { myIsFindInProgress = false; diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java index f5c006037247..f769247efb3d 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java @@ -419,8 +419,6 @@ public class FindDialog extends DialogWrapper { ValidationInfo result = getValidationInfo(modelClone); - final PsiDirectory psiDirectory = FindInProjectUtil.getPsiDirectory(modelClone, myProject); - final ProgressIndicatorBase progressIndicatorWhenSearchStarted = new ProgressIndicatorBase(); myResultsPreviewSearchProgress = progressIndicatorWhenSearchStarted; @@ -460,7 +458,7 @@ public class FindDialog extends DialogWrapper { final FindUsagesProcessPresentation processPresentation = FindInProjectUtil.setupProcessPresentation(myProject, showPanelIfOnlyOneUsage, presentation); - FindInProjectUtil.findUsages(modelClone, psiDirectory, myProject, new Processor() { + FindInProjectUtil.findUsages(modelClone, myProject, new Processor() { @Override public boolean process(final UsageInfo info) { final Usage usage = UsageInfo2UsageAdapter.CONVERTER.fun(info); @@ -822,7 +820,7 @@ public class FindDialog extends DialogWrapper { @Nullable("null means OK") private ValidationInfo getValidationInfo(@NotNull FindModel model) { if (myRbDirectory != null && myRbDirectory.isEnabled() && myRbDirectory.isSelected()) { - PsiDirectory directory = FindInProjectUtil.getPsiDirectory(model, myProject); + VirtualFile directory = FindInProjectUtil.getDirectory(model); if (directory == null) { return new ValidationInfo(FindBundle.message("find.directory.not.found.error", getDirectory()), myDirectoryComboBox); } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java index 97da7e3bd22b..8821e1018aa7 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java @@ -76,7 +76,7 @@ class FindInProjectTask { private final FindModel myFindModel; private final Project myProject; private final PsiManager myPsiManager; - @Nullable private final PsiDirectory myPsiDirectory; + @Nullable private final VirtualFile myDirectory; private final ProjectFileIndex myProjectFileIndex; private final FileIndex myFileIndex; private final Condition myFileMask; @@ -85,12 +85,10 @@ class FindInProjectTask { private final Set myLargeFiles = ContainerUtil.newTroveSet(); private boolean myWarningShown; - FindInProjectTask(@NotNull final FindModel findModel, - @NotNull final Project project, - @Nullable final PsiDirectory psiDirectory) { + FindInProjectTask(@NotNull final FindModel findModel, @NotNull final Project project) { myFindModel = findModel; myProject = project; - myPsiDirectory = psiDirectory; + myDirectory = FindInProjectUtil.getDirectory(findModel); myPsiManager = PsiManager.getInstance(project); final String moduleName = findModel.getModuleName(); @@ -267,7 +265,7 @@ class FindInProjectTask { @NotNull private Collection collectFilesInScope(@NotNull final Set alreadySearched, final boolean skipIndexed) { SearchScope customScope = myFindModel.isCustomScope() ? myFindModel.getCustomScope() : null; - final GlobalSearchScope globalCustomScope = toGlobal(customScope); + final GlobalSearchScope globalCustomScope = customScope == null ? null : toGlobal(customScope); final ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(myProject); final boolean hasTrigrams = hasTrigrams(myFindModel.getStringToFind()); @@ -327,12 +325,13 @@ class FindInProjectTask { iterator.processFile(file); } } - else if (myPsiDirectory != null) { + else if (myDirectory != null) { + final boolean checkExcluded = !ProjectFileIndex.SERVICE.getInstance(myProject).isExcluded(myDirectory); VirtualFileVisitor.Option limit = VirtualFileVisitor.limit(myFindModel.isWithSubdirectories() ? -1 : 1); - VfsUtilCore.visitChildrenRecursively(myPsiDirectory.getVirtualFile(), new VirtualFileVisitor(limit) { + VfsUtilCore.visitChildrenRecursively(myDirectory, new VirtualFileVisitor(limit) { @Override public boolean visitFile(@NotNull VirtualFile file) { - if (myProjectFileIndex.isExcluded(file)) return false; + if (checkExcluded && myProjectFileIndex.isExcluded(file)) return false; iterator.processFile(file); return true; } @@ -369,9 +368,9 @@ class FindInProjectTask { return true; } - @Nullable - private GlobalSearchScope toGlobal(@Nullable final SearchScope scope) { - if (scope instanceof GlobalSearchScope || scope == null) { + @NotNull + private GlobalSearchScope toGlobal(@NotNull final SearchScope scope) { + if (scope instanceof GlobalSearchScope) { return (GlobalSearchScope)scope; } return ApplicationManager.getApplication().runReadAction(new Computable() { diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java index 2b5e9f77f929..dafb6d4b36cb 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java @@ -119,14 +119,22 @@ public class FindInProjectUtil { } } + /** + * @deprecated to remove in IDEA 16 + */ @Nullable public static PsiDirectory getPsiDirectory(@NotNull final FindModel findModel, @NotNull Project project) { + VirtualFile directory = getDirectory(findModel); + return directory == null ? null : PsiManager.getInstance(project).findDirectory(directory); + } + + @Nullable + public static VirtualFile getDirectory(@NotNull final FindModel findModel) { String directoryName = findModel.getDirectoryName(); if (findModel.isProjectScope() || StringUtil.isEmpty(directoryName)) { return null; } - final PsiManager psiManager = PsiManager.getInstance(project); String path = directoryName.replace(File.separatorChar, '/'); VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path); if (virtualFile == null || !virtualFile.isDirectory()) { @@ -139,12 +147,13 @@ public class FindInProjectUtil { break; } if(virtualFile == null){ - virtualFile = file; + virtualFile = file; } } } } - return virtualFile == null ? null : psiManager.findDirectory(virtualFile); + return virtualFile; + } @@ -170,12 +179,22 @@ public class FindInProjectUtil { return Pattern.compile(pattern, Pattern.CASE_INSENSITIVE); } + /** + * @deprecated to remove in IDEA 16 + */ public static void findUsages(@NotNull FindModel findModel, - final PsiDirectory psiDirectory, + @Nullable final PsiDirectory psiDirectory, @NotNull final Project project, @NotNull final Processor consumer, @NotNull FindUsagesProcessPresentation processPresentation) { - new FindInProjectTask(findModel, project, psiDirectory).findUsages(consumer, processPresentation); + findUsages(findModel, project, consumer, processPresentation); + } + + public static void findUsages(@NotNull FindModel findModel, + @NotNull final Project project, + @NotNull final Processor consumer, + @NotNull FindUsagesProcessPresentation processPresentation) { + new FindInProjectTask(findModel, project).findUsages(consumer, processPresentation); } static int processUsagesInFile(@NotNull final PsiFile psiFile, @@ -453,7 +472,7 @@ public class FindInProjectUtil { ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(project); VirtualFile classRoot = index.getClassRootForFile(file); if (classRoot == null) return; - String relativePath = VfsUtil.getRelativePath(file, classRoot); + String relativePath = VfsUtilCore.getRelativePath(file, classRoot); if (relativePath == null) return; for (OrderEntry orderEntry : index.getOrderEntriesForFile(file)) { for (VirtualFile sourceRoot : orderEntry.getFiles(OrderRootType.SOURCES)) { @@ -468,8 +487,7 @@ public class FindInProjectUtil { @NotNull static SearchScope getScopeFromModel(@NotNull Project project, @NotNull FindModel findModel) { SearchScope customScope = findModel.getCustomScope(); - PsiDirectory psiDir = getPsiDirectory(findModel, project); - VirtualFile directory = psiDir == null ? null : psiDir.getVirtualFile(); + VirtualFile directory = getDirectory(findModel); Module module = findModel.getModuleName() == null ? null : ModuleManager.getInstance(project).findModuleByName(findModel.getModuleName()); return findModel.isCustomScope() && customScope != null ? customScope : // we don't have to check for myProjectFileIndex.isExcluded(file) here like FindInProjectTask.collectFilesInScope() does diff --git a/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java b/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java index 57a299788e9b..73ab3bbcd0f7 100644 --- a/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java +++ b/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java @@ -45,7 +45,6 @@ import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.StatusBar; import com.intellij.openapi.wm.WindowManager; -import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -141,9 +140,8 @@ public class ReplaceInProjectManager { findManager.showFindDialog(findModel, new Runnable() { @Override public void run() { - final PsiDirectory psiDirectory = FindInProjectUtil.getPsiDirectory(findModel, myProject); if (!findModel.isProjectScope() && - psiDirectory == null && + FindInProjectUtil.getDirectory(findModel) == null && findModel.getModuleName() == null && findModel.getCustomScope() == null) { return; @@ -158,7 +156,7 @@ public class ReplaceInProjectManager { final UsageViewPresentation presentation = FindInProjectUtil.setupViewPresentation(findModel.isOpenInNewTab(), findModelCopy); final FindUsagesProcessPresentation processPresentation = FindInProjectUtil.setupProcessPresentation(myProject, true, presentation); - UsageSearcherFactory factory = new UsageSearcherFactory(findModelCopy, psiDirectory, processPresentation); + UsageSearcherFactory factory = new UsageSearcherFactory(findModelCopy, processPresentation); searchAndShowUsages(manager, factory, findModelCopy, presentation, processPresentation, findManager); } }); @@ -583,14 +581,11 @@ public class ReplaceInProjectManager { private class UsageSearcherFactory implements Factory { private final FindModel myFindModelCopy; - private final PsiDirectory myPsiDirectory; private final FindUsagesProcessPresentation myProcessPresentation; private UsageSearcherFactory(@NotNull FindModel findModelCopy, - PsiDirectory psiDirectory, @NotNull FindUsagesProcessPresentation processPresentation) { myFindModelCopy = findModelCopy; - myPsiDirectory = psiDirectory; myProcessPresentation = processPresentation; } @@ -603,7 +598,7 @@ public class ReplaceInProjectManager { try { myIsFindInProgress = true; - FindInProjectUtil.findUsages(myFindModelCopy, myPsiDirectory, myProject, + FindInProjectUtil.findUsages(myFindModelCopy, myProject, new AdapterProcessor(processor, UsageInfo2UsageAdapter.CONVERTER), myProcessPresentation); } diff --git a/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java b/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java index 8bcc058fbf2f..c7c169a323b9 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java +++ b/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java @@ -37,6 +37,8 @@ import com.intellij.openapi.ui.InputValidator; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.Trinity; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; @@ -63,6 +65,7 @@ public class BookmarkManager extends AbstractProjectComponent implements Persist private static final int MAX_AUTO_DESCRIPTION_SIZE = 50; private final List myBookmarks = new ArrayList(); + private final Map, Bookmark> myDeletedDocumentBookmarks = new HashMap, Bookmark>(); private final MessageBus myBus; @@ -479,7 +482,7 @@ public class BookmarkManager extends AbstractProjectComponent implements Persist @Override public void beforeDocumentChange(DocumentEvent e) { if (e.isWholeTextReplaced()) return; - List bookmarksToRemove = null; + Map, Bookmark> bookmarksToRemove = null; for (Bookmark bookmark : myBookmarks) { Document document = bookmark.getDocument(); if (document == null || document != e.getDocument()) continue; @@ -488,15 +491,18 @@ public class BookmarkManager extends AbstractProjectComponent implements Persist int start = document.getLineStartOffset(bookmark.getLine()); int end = document.getLineEndOffset(bookmark.getLine()); if (start >= e.getOffset() && end <= e.getOffset() + e.getOldLength() ) { + Trinity restoreBookmarkData + = new Trinity(bookmark.getFile(), bookmark.getLine(), document.getText(new TextRange(start, end))); if (bookmarksToRemove == null) { - bookmarksToRemove = new ArrayList(); + bookmarksToRemove = new HashMap, Bookmark>(); } - bookmarksToRemove.add(bookmark); + bookmarksToRemove.put(restoreBookmarkData, bookmark); } } if (bookmarksToRemove != null) { - for (Bookmark bookmark : bookmarksToRemove) { - removeBookmark(bookmark); + for (Map.Entry, Bookmark> entry : bookmarksToRemove.entrySet()) { + removeBookmark(entry.getValue()); + myDeletedDocumentBookmarks.put(entry.getKey(), entry.getValue()); } } } @@ -531,6 +537,36 @@ public class BookmarkManager extends AbstractProjectComponent implements Persist removeBookmark(bookmark); } } + + for (Iterator, Bookmark>> iterator = myDeletedDocumentBookmarks.entrySet().iterator(); + iterator.hasNext(); ) { + Map.Entry, Bookmark> entry = iterator.next(); + + if (!entry.getKey().first.isValid()) { + iterator.remove(); + continue; + } + + Bookmark bookmark = entry.getValue(); + Document document = bookmark.getDocument(); + if (document == null || !bookmark.getFile().equals(entry.getKey().first)) { + continue; + } + Integer line = entry.getKey().second; + if (document.getLineCount() <= line) { + continue; + } + int start = document.getLineStartOffset(line); + int end = document.getLineEndOffset(line); + String lineContent = document.getText(new TextRange(start, end)); + if (entry.getKey().third.equals(lineContent)) { + Bookmark restored = addTextBookmark(bookmark.getFile(), bookmark.getLine(), bookmark.getDescription()); + if (bookmark.getMnemonic() != 0) { + setMnemonic(restored, bookmark.getMnemonic()); + } + iterator.remove(); + } + } } } } diff --git a/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java b/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java index 8ca50f506af1..fe6ce0840659 100644 --- a/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java +++ b/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java @@ -14,8 +14,6 @@ * limitations under the License. */ -// This class is a runtime engine for parsers generated by Grammar-Kit. -// Do not modify or refactor without complete investigation and/or review. package com.intellij.lang.parser; import com.intellij.codeInsight.completion.impl.CamelHumpMatcher; @@ -50,13 +48,21 @@ import java.util.Collection; import java.util.LinkedList; /** + * This class is a runtime engine for parsers generated by
    Grammar-Kit. + *
    + * Here is the original + * GeneratedParserUtilBase code. + *
    + *
    + * CAUTION: Authorized personnel only. Do not modify or refactor. + * * @author gregsh */ public class GeneratedParserUtilBase { private static final Logger LOG = Logger.getInstance("org.intellij.grammar.parser.GeneratedParserUtilBase"); - private static final int MAX_RECURSION_LEVEL = 1000; + private static final int MAX_RECURSION_LEVEL = StringUtil.parseInt(System.getProperty("grammar.kit.gpub.max.level"), 1000); private static final int MAX_VARIANTS_SIZE = 10000; private static final int MAX_VARIANTS_TO_DISPLAY = 50; @@ -116,7 +122,7 @@ public class GeneratedParserUtilBase { if (!goodMarker) return false; ErrorState state = ErrorState.get(builder); - return !state.frameStack.isEmpty(); + return state.currentFrame != null; } public static TokenSet create_token_set_(IElementType... tokenTypes) { @@ -396,6 +402,7 @@ public class GeneratedParserUtilBase { public static final int _LEFT_INNER_ = 0x4; public static final int _AND_ = 0x8; public static final int _NOT_ = 0x10; + public static final int _UPPER_ = 0x20; // simple enter/exit methods pair that doesn't require frame object public static PsiBuilder.Marker enter_section_(PsiBuilder builder) { @@ -406,20 +413,28 @@ public class GeneratedParserUtilBase { PsiBuilder.Marker marker, @Nullable IElementType elementType, boolean result) { - close_marker_impl_(ErrorState.get(builder).frameStack.peekLast(), marker, elementType, result); + close_marker_impl_(ErrorState.get(builder).currentFrame, marker, elementType, result); } // complex enter/exit methods pair with frame object - public static PsiBuilder.Marker enter_section_(PsiBuilder builder, int level, int modifiers, @Nullable String frameName) { + public static PsiBuilder.Marker enter_section_(PsiBuilder builder, int level, int modifiers, String frameName) { + return enter_section_(builder, level, modifiers, null, frameName); + } + + public static PsiBuilder.Marker enter_section_(PsiBuilder builder, int level, int modifiers) { + return enter_section_(builder, level, modifiers, null, null); + } + + public static PsiBuilder.Marker enter_section_(PsiBuilder builder, int level, int modifiers, IElementType elementType, String frameName) { PsiBuilder.Marker marker = builder.mark(); - enter_section_impl_(builder, level, modifiers, frameName); + enter_section_impl_(builder, level, modifiers, elementType, frameName); return marker; } - private static void enter_section_impl_(PsiBuilder builder, int level, int modifiers, @Nullable String frameName) { + private static void enter_section_impl_(PsiBuilder builder, int level, int modifiers, IElementType elementType, String frameName) { ErrorState state = ErrorState.get(builder); - Frame frame = state.FRAMES.alloc().init(builder, state, level, modifiers, frameName); - Frame prevFrame = state.frameStack.peekLast(); + Frame frame = state.FRAMES.alloc().init(builder, state, level, modifiers, elementType, frameName); + Frame prevFrame = state.currentFrame; if (prevFrame != null && prevFrame.errorReportedAt > frame.position) { // report error for previous unsuccessful frame reportError(builder, state, frame, null, true, false); @@ -430,7 +445,7 @@ public class GeneratedParserUtilBase { frame.leftMarker = left; } } - state.frameStack.add(frame); + state.currentFrame = frame; if ((modifiers & _AND_) != 0) { if (state.predicateCount == 0 && !state.predicateSign) { throw new AssertionError("Incorrect false predicate sign"); @@ -448,6 +463,15 @@ public class GeneratedParserUtilBase { } } + public static void exit_section_(PsiBuilder builder, + int level, + PsiBuilder.Marker marker, + boolean result, + boolean pinned, + @Nullable Parser eatMore) { + exit_section_(builder, level, marker, null, result, pinned, eatMore); + } + public static void exit_section_(PsiBuilder builder, int level, PsiBuilder.Marker marker, @@ -457,7 +481,9 @@ public class GeneratedParserUtilBase { @Nullable Parser eatMore) { ErrorState state = ErrorState.get(builder); - Frame frame = state.frameStack.pollLast(); + Frame frame = state.currentFrame; + state.currentFrame = frame == null ? null : frame.parentFrame; + if (frame != null && frame.elementType != null) elementType = frame.elementType; if (frame == null || level != frame.level) { LOG.error("Unbalanced error section: got " + frame + ", expected level " + level); if (frame != null) state.FRAMES.recycle(frame); @@ -469,11 +495,11 @@ public class GeneratedParserUtilBase { close_marker_impl_(frame, marker, null, false); state.predicateCount--; if ((frame.modifiers & _NOT_) != 0) state.predicateSign = !state.predicateSign; - state.FRAMES.recycle(frame); - return; } - close_frame_impl_(state, frame, builder, marker, elementType, result, pinned); - exit_section_impl_(state, frame, builder, elementType, result, pinned, eatMore); + else { + close_frame_impl_(state, frame, builder, marker, elementType, result, pinned); + exit_section_impl_(state, frame, builder, elementType, result, pinned, eatMore); + } state.FRAMES.recycle(frame); } @@ -514,10 +540,12 @@ public class GeneratedParserUtilBase { // skip tokens until lastErrorPos. parseAsTree might look better here... int parenCount = 0; while ((eatMoreFlag || parenCount > 0) && builder.rawTokenIndex() < lastErrorPos) { + IElementType tokenType = builder.getTokenType(); if (state.braces != null) { - if (builder.getTokenType() == state.braces[0].getLeftBraceType()) parenCount ++; - else if (builder.getTokenType() == state.braces[0].getRightBraceType()) parenCount --; + if (tokenType == state.braces[0].getLeftBraceType()) parenCount ++; + else if (tokenType == state.braces[0].getRightBraceType()) parenCount --; } + if (!(builder.rawTokenIndex() < lastErrorPos)) break; builder.advanceLexer(); eatMoreFlag = eatMore.parse(builder, frame.level + 1); } @@ -535,6 +563,9 @@ public class GeneratedParserUtilBase { else if (eatMoreFlagOnce || (!result && frame.position != builder.rawTokenIndex()) || frame.errorReportedAt > initialPos) { errorReported = reportError(builder, state, frame, null, true, false); } + else if (!result && pinned && frame.errorReportedAt < 0) { + errorReported = reportError(builder, state, frame, elementType, false, false); + } if (extensionMarker != null) { extensionMarker.done(extensionTokenType); } @@ -557,7 +588,7 @@ public class GeneratedParserUtilBase { } } // propagate errorReportedAt up the stack to avoid duplicate reporting - Frame prevFrame = willFail && eatMore == null ? null : state.frameStack.peekLast(); + Frame prevFrame = willFail && eatMore == null ? null : state.currentFrame; if (prevFrame != null && prevFrame.errorReportedAt < frame.errorReportedAt) { prevFrame.errorReportedAt = frame.errorReportedAt; } @@ -582,7 +613,15 @@ public class GeneratedParserUtilBase { } } if (result || pinned) { - if ((frame.modifiers & _LEFT_INNER_) != 0 && frame.leftMarker != null) { + if ((frame.modifiers & _UPPER_) != 0) { + marker.drop(); + for (Frame f = frame.parentFrame; f != null; f = f.parentFrame) { + if (f.elementType == null) continue; + f.elementType = elementType; + break; + } + } + else if ((frame.modifiers & _LEFT_INNER_) != 0 && frame.leftMarker != null) { marker.done(elementType); frame.leftMarker.precede().done(((LighterASTNode)frame.leftMarker).getTokenType()); frame.leftMarker.drop(); @@ -625,8 +664,8 @@ public class GeneratedParserUtilBase { else { if (frame != null) { int position = ((PsiBuilderImpl.ProductionMarker)marker).getStartIndex(); - if (frame.errorReportedAt > position) { - frame.errorReportedAt = frame.errorReportedAtPrev; + if (frame.errorReportedAt > position && frame.parentFrame != null) { + frame.errorReportedAt = frame.parentFrame.errorReportedAt; } } marker.rollbackTo(); @@ -639,7 +678,7 @@ public class GeneratedParserUtilBase { } public static void report_error_(PsiBuilder builder, ErrorState state, boolean advance) { - Frame frame = state.frameStack.isEmpty()? null : state.frameStack.getLast(); + Frame frame = state.currentFrame; if (frame == null) { LOG.error("unbalanced enter/exit section call: got null"); return; @@ -662,38 +701,37 @@ public class GeneratedParserUtilBase { boolean advance) { String expectedText = state.getExpectedText(builder); boolean notEmpty = StringUtil.isNotEmpty(expectedText); - if (force || notEmpty || advance) { - String gotText = builder.eof()? "unexpected end of file" : - notEmpty? "got '" + builder.getTokenText() +"'" : - "'" + builder.getTokenText() +"' unexpected"; - String message = expectedText + gotText; - if (advance) { - PsiBuilder.Marker mark = builder.mark(); - builder.advanceLexer(); - mark.error(message); - } - else if (!force) { - PsiBuilder.Marker extensionMarker = null; - IElementType extensionTokenType = null; - PsiBuilderImpl.ProductionMarker latestDoneMarker = elementType == null ? null : (PsiBuilderImpl.ProductionMarker)builder.getLatestDoneMarker(); - if (latestDoneMarker != null && - frame.position >= latestDoneMarker.getStartIndex() && - frame.position <= latestDoneMarker.getEndIndex()) { - extensionMarker = ((PsiBuilder.Marker)latestDoneMarker).precede(); - extensionTokenType = latestDoneMarker.getTokenType(); - ((PsiBuilder.Marker)latestDoneMarker).drop(); - } - builder.error(message); - if (extensionMarker != null) extensionMarker.done(extensionTokenType); - } - else { - builder.error(message); - } - builder.eof(); // skip whitespaces - frame.errorReportedAt = builder.rawTokenIndex(); - return true; + if (!(force || notEmpty || advance)) return false; + + String gotText = builder.eof() ? "unexpected end of file" : + notEmpty? "got '" + builder.getTokenText() +"'" : + "'" + builder.getTokenText() +"' unexpected"; + String message = expectedText + gotText; + if (advance) { + PsiBuilder.Marker mark = builder.mark(); + builder.advanceLexer(); + mark.error(message); } - return false; + else if (!force) { + PsiBuilder.Marker extensionMarker = null; + IElementType extensionTokenType = null; + PsiBuilderImpl.ProductionMarker latestDoneMarker = elementType == null ? null : (PsiBuilderImpl.ProductionMarker)builder.getLatestDoneMarker(); + if (latestDoneMarker != null && + frame.position >= latestDoneMarker.getStartIndex() && + frame.position <= latestDoneMarker.getEndIndex()) { + extensionMarker = ((PsiBuilder.Marker)latestDoneMarker).precede(); + extensionTokenType = latestDoneMarker.getTokenType(); + ((PsiBuilder.Marker)latestDoneMarker).drop(); + } + builder.error(message); + if (extensionMarker != null) extensionMarker.done(extensionTokenType); + } + else { + builder.error(message); + } + builder.eof(); // skip whitespaces + frame.errorReportedAt = builder.rawTokenIndex(); + return true; } @@ -762,7 +800,7 @@ public class GeneratedParserUtilBase { int predicateCount; boolean predicateSign = true; boolean suppressErrors; - public final LinkedList frameStack = new LinkedList(); + public Frame currentFrame; public CompletionState completionState; private boolean caseSensitive; @@ -781,7 +819,7 @@ public class GeneratedParserUtilBase { } @Override - public void cleanup(@NotNull final Variant o) { + public void cleanup(@NotNull Variant o) { } }); final LimitedPool FRAMES = new LimitedPool(FRAMES_POOL_SIZE, new LimitedPool.ObjectFactory() { @@ -792,7 +830,7 @@ public class GeneratedParserUtilBase { } @Override - public void cleanup(@NotNull final Frame o) { + public void cleanup(@NotNull Frame o) { } }); @@ -862,23 +900,17 @@ public class GeneratedParserUtilBase { return count > 0; } - public int getVariantsSize() { - return variants.size(); + public void clearVariants(Frame frame) { + clearVariants(true, frame == null ? 0 : frame.variantCount); } - public void clearVariants(boolean expected, int start) { + void clearVariants(boolean expected, int start) { MyList list = expected? variants : unexpected; if (start < 0 || start >= list.size()) return; for (int i = start, len = list.size(); i < len; i ++) { VARIANTS.recycle(list.get(i)); } list.setSize(start); - if (expected) { - lastExpectedVariantPos = -1; - for (Variant variant : list) { - if (lastExpectedVariantPos < variant.position) lastExpectedVariantPos = variant.position; - } - } } public boolean typeExtends(IElementType child, IElementType parent) { @@ -893,6 +925,9 @@ public class GeneratedParserUtilBase { } public static class Frame { + public Frame parentFrame; + public IElementType elementType; + public int offset; public int position; public int level; @@ -900,13 +935,20 @@ public class GeneratedParserUtilBase { public String name; public int variantCount; public int errorReportedAt; - public int errorReportedAtPrev; public PsiBuilder.Marker leftMarker; public Frame() { } - public Frame init(PsiBuilder builder, ErrorState state, int level_, int modifiers_, String name_) { + public Frame init(PsiBuilder builder, + ErrorState state, + int level_, + int modifiers_, + IElementType elementType_, + String name_) { + parentFrame = state.currentFrame; + elementType = elementType_; + offset = builder.getCurrentOffset(); position = builder.rawTokenIndex(); level = level_; @@ -915,8 +957,6 @@ public class GeneratedParserUtilBase { variantCount = state.variants.size(); errorReportedAt = -1; - Frame prev = state.frameStack.peekLast(); - errorReportedAtPrev = prev == null? -1 : prev.errorReportedAt; leftMarker = null; return this; } @@ -928,8 +968,9 @@ public class GeneratedParserUtilBase { ((modifiers & _LEFT_) != 0? "_LEFT_, ": "") + ((modifiers & _LEFT_INNER_) != 0? "_LEFT_INNER_, ": "") + ((modifiers & _AND_) != 0? "_AND_, ": "") + - ((modifiers & _NOT_) != 0? "_NOT_, ": ""); - return String.format("{%s:%s:%d, %d, %s%s}", offset, position, level, errorReportedAt, mod, name); + ((modifiers & _NOT_) != 0? "_NOT_, ": "") + + ((modifiers & _UPPER_) != 0 ? "_UPPER_, " : ""); + return String.format("{%s:%s:%d, %d, %s%s, %s}", offset, position, level, errorReportedAt, mod, elementType, name); } } diff --git a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java index 418aa3cf1df7..3cc712c996c3 100644 --- a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java +++ b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java @@ -132,6 +132,10 @@ public class ModuleManagerComponent extends ModuleManagerImpl { @Override protected void fireModulesAdded() { + if (myModuleModel.myModules.isEmpty()) { + return; + } + Runnable runnableWithProgress = new Runnable() { @Override public void run() { diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/IDEInspectionToolsConfigurable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/IDEInspectionToolsConfigurable.java index 82da5217a6fc..04a271030d48 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/IDEInspectionToolsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/IDEInspectionToolsConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,20 +24,20 @@ import com.intellij.codeInspection.ex.InspectionProfileImpl; import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.profile.codeInspection.ui.header.InspectionToolsConfigurable; +import org.jetbrains.annotations.NotNull; public class IDEInspectionToolsConfigurable extends InspectionToolsConfigurable { - public IDEInspectionToolsConfigurable(InspectionProjectProfileManager projectProfileManager, InspectionProfileManager profileManager) { super(projectProfileManager, profileManager); } @Override protected InspectionProfileImpl getCurrentProfile() { - return (InspectionProfileImpl)myProfileManager.getRootProfile(); + return (InspectionProfileImpl)myApplicationProfileManager.getRootProfile(); } @Override - protected void applyRootProfile(String name, boolean isShared) { - myProfileManager.setRootProfile(name); + protected void applyRootProfile(@NotNull String name, boolean isProjectLevel) { + myApplicationProfileManager.setRootProfile(name); } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ProjectInspectionToolsConfigurable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ProjectInspectionToolsConfigurable.java index 280dee11d6e8..82ccbfe4a7b3 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ProjectInspectionToolsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ProjectInspectionToolsConfigurable.java @@ -25,6 +25,7 @@ import com.intellij.codeInspection.ex.InspectionProfileManagerImpl; import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.profile.codeInspection.ui.header.InspectionToolsConfigurable; +import org.jetbrains.annotations.NotNull; public class ProjectInspectionToolsConfigurable extends InspectionToolsConfigurable { public ProjectInspectionToolsConfigurable(InspectionProfileManager profileManager, InspectionProjectProfileManager projectProfileManager) { @@ -37,12 +38,12 @@ public class ProjectInspectionToolsConfigurable extends InspectionToolsConfigura } @Override - protected void applyRootProfile(String name, boolean isShared) { - if (isShared) { + protected void applyRootProfile(@NotNull String name, boolean isProjectLevel) { + if (isProjectLevel) { myProjectProfileManager.setProjectProfile(name); } else { - myProfileManager.setRootProfile(name); + myApplicationProfileManager.setRootProfile(name); myProjectProfileManager.setProjectProfile(null); } InspectionProfileManagerImpl.onProfilesChanged(); diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java index d456b06c70d3..9f3a2bb7bbe7 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java @@ -129,7 +129,7 @@ public class SingleInspectionProfilePanel extends JPanel { @NotNull private String myCurrentProfileName; private boolean myIsInRestore = false; - private boolean myShareProfile; + private boolean myIsProjectLevel; private JBSplitter myRightSplitter; private JBSplitter myMainSplitter; @@ -148,7 +148,7 @@ public class SingleInspectionProfilePanel extends JPanel { myOriginal = original; mySelectedProfile = (InspectionProfileImpl)profile; myCurrentProfileName = inspectionProfileName; - myShareProfile = profile.getProfileManager() == projectProfileManager; + myIsProjectLevel = profile.getProfileManager() == projectProfileManager; } private static VisibleTreeState getExpandedNodes(InspectionProfileImpl profile) { @@ -1112,7 +1112,7 @@ public class SingleInspectionProfilePanel extends JPanel { public boolean isModified() { if (myModified) return true; if (mySelectedProfile.isChanged()) return true; - if (myShareProfile != (mySelectedProfile.getProfileManager() == myProjectProfileManager)) return true; + if (myIsProjectLevel != (mySelectedProfile.getProfileManager() == myProjectProfileManager)) return true; if (!Comparing.strEqual(myCurrentProfileName, mySelectedProfile.getName())) return true; if (!Comparing.equal(myInitialScopesOrder, mySelectedProfile.getScopesOrder())) return true; return descriptorsAreChanged(); @@ -1124,7 +1124,7 @@ public class SingleInspectionProfilePanel extends JPanel { final String filter = myProfileFilter.getFilter(); myProfileFilter.reset(); myProfileFilter.setSelectedItem(filter); - myShareProfile = mySelectedProfile.getProfileManager() == myProjectProfileManager; + myIsProjectLevel = mySelectedProfile.getProfileManager() == myProjectProfileManager; } public void apply() throws ConfigurationException { @@ -1134,8 +1134,8 @@ public class SingleInspectionProfilePanel extends JPanel { } final ModifiableModel selectedProfile = getSelectedProfile(); - ProfileManager profileManager = myShareProfile ? myProjectProfileManager : InspectionProfileManager.getInstance(); - selectedProfile.setProjectLevel(myShareProfile); + ProfileManager profileManager = myIsProjectLevel ? myProjectProfileManager : InspectionProfileManager.getInstance(); + selectedProfile.setProjectLevel(myIsProjectLevel); if (selectedProfile.getProfileManager() != profileManager) { if (selectedProfile.getProfileManager().getProfile(selectedProfile.getName(), false) == myOriginal) { selectedProfile.getProfileManager().deleteProfile(selectedProfile.getName()); @@ -1202,12 +1202,12 @@ public class SingleInspectionProfilePanel extends JPanel { return false; } - public boolean isProfileShared() { - return myShareProfile; + public boolean isProjectLevel() { + return myIsProjectLevel; } - public void setProfileShared(boolean profileShared) { - myShareProfile = profileShared; + public void setIsProjectLevel(boolean value) { + myIsProjectLevel = value; } @NotNull diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java index f9924903a312..b5193eddcedf 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java @@ -83,7 +83,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable private static final String HEADER_TITLE = "Profile:"; private static final Logger LOG = Logger.getInstance(InspectionToolsConfigurable.class); - protected final InspectionProfileManager myProfileManager; + protected final InspectionProfileManager myApplicationProfileManager; protected final InspectionProjectProfileManager myProjectProfileManager; private final CardLayout myLayout = new CardLayout(); private final Map myPanels = @@ -97,7 +97,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable public InspectionToolsConfigurable(@NotNull final InspectionProjectProfileManager projectProfileManager, InspectionProfileManager profileManager) { myProjectProfileManager = projectProfileManager; - myProfileManager = profileManager; + myApplicationProfileManager = profileManager; } private static JComponent withBorderOnTop(final JComponent component) { @@ -117,7 +117,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable do { profileDefaultName += " (copy)"; } - while (hasName(profileDefaultName, myPanels.get(selectedProfile).isProfileShared())); + while (hasName(profileDefaultName, myPanels.get(selectedProfile).isProjectLevel())); final ProfileManager profileManager = selectedProfile.getProfileManager(); InspectionProfileImpl inspectionProfile = @@ -208,7 +208,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable value.getName(), value.getProfileManager().getClass(), ((InspectionProfileImpl) value).isChanged())); - final boolean isShared = singleInspectionProfilePanel.isProfileShared(); + final boolean isShared = singleInspectionProfilePanel.isProjectLevel(); setIcon(isShared ? AllIcons.General.ProjectSettings : AllIcons.General.Settings); setText(singleInspectionProfilePanel.getCurrentProfileName()); } @@ -225,28 +225,28 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable JComponent manageButton = new ManageButton(new ManageButtonBuilder() { @Override - public boolean isSharedToTeamMembers() { + public boolean isProjectLevel() { SingleInspectionProfilePanel panel = getSelectedPanel(); - return panel != null && panel.isProfileShared(); + return panel != null && panel.isProjectLevel(); } @Override - public void setShareToTeamMembers(boolean shared) { + public void setIsProjectLevel(boolean isProjectLevel) { final SingleInspectionProfilePanel selectedPanel = getSelectedPanel(); LOG.assertTrue(selectedPanel != null, "No settings selectedPanel for: " + getSelectedObject()); final String name = getSelectedPanel().getCurrentProfileName(); for (SingleInspectionProfilePanel p : myPanels.values()) { if (p != selectedPanel && Comparing.equal(p.getCurrentProfileName(), name)) { - final boolean curShared = p.isProfileShared(); - if (curShared == shared) { - Messages.showErrorDialog((shared ? "Shared" : "Application level") + " profile with same name exists.", "Inspections Settings"); + final boolean curShared = p.isProjectLevel(); + if (curShared == isProjectLevel) { + Messages.showErrorDialog((isProjectLevel ? "Shared" : "Application level") + " profile with same name exists.", "Inspections Settings"); return; } } } - selectedPanel.setProfileShared(shared); + selectedPanel.setIsProjectLevel(isProjectLevel); myProfiles.repaint(); } @@ -290,7 +290,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable if (singleInspectionProfilePanel == null) { return false; } - final boolean isValid = text.equals(initialName) || !hasName(text, singleInspectionProfilePanel.isProfileShared()); + final boolean isValid = text.equals(initialName) || !hasName(text, singleInspectionProfilePanel.isProjectLevel()); if (isValid) { myAuxiliaryRightPanel.showDescription(getSelectedObject().getDescription()); } @@ -386,7 +386,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable final InspectionProfileImpl profile; try { Element rootElement = JDOMUtil.loadDocument(VfsUtilCore.virtualToIoFile(file)).getRootElement(); - profile = importInspectionProfile(rootElement, myProfileManager, getProject(), wholePanel); + profile = importInspectionProfile(rootElement, myApplicationProfileManager, getProject(), wholePanel); if (getProfilePanel(profile) != null) { if (Messages.showOkCancelDialog(wholePanel, "Profile with name \'" + profile.getName() + @@ -523,14 +523,14 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable final SingleInspectionProfilePanel panel = getProfilePanel(inspectionProfile); panel.apply(); if (panel == selectedPanel) { - applyRootProfile(panel.getCurrentProfileName(), panel.isProfileShared()); + applyRootProfile(panel.getCurrentProfileName(), panel.isProjectLevel()); } } } doReset(); } - protected abstract void applyRootProfile(final String name, final boolean isShared); + protected abstract void applyRootProfile(@NotNull String name, boolean isProjectLevel); private SingleInspectionProfilePanel getProfilePanel(Profile inspectionProfile) { return myPanels.get(inspectionProfile); @@ -542,9 +542,9 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable private void deleteProfile(Profile profile) { final String name = profile.getName(); - if (profile.getProfileManager() == myProfileManager) { - if (myProfileManager.getProfile(name, false) != null) { - myProfileManager.deleteProfile(name); + if (profile.getProfileManager() == myApplicationProfileManager) { + if (myApplicationProfileManager.getProfile(name, false) != null) { + myApplicationProfileManager.deleteProfile(name); } return; } @@ -632,7 +632,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable protected Collection getProfiles() { final Collection result = new ArrayList(); - result.addAll(new TreeSet(myProfileManager.getProfiles())); + result.addAll(new TreeSet(myApplicationProfileManager.getProfiles())); result.addAll(myProjectProfileManager.getProfiles()); return result; } @@ -673,7 +673,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable private boolean hasName(@NotNull final String name, boolean shared) { for (SingleInspectionProfilePanel p : myPanels.values()) { - if (name.equals(p.getCurrentProfileName()) && shared == p.isProfileShared()) { + if (name.equals(p.getCurrentProfileName()) && shared == p.isProjectLevel()) { return true; } } diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/ManageButton.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/ManageButton.java index 5b55ea6b6c25..48d7fcf71fdd 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/ManageButton.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/ManageButton.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,12 +69,12 @@ public class ManageButton extends ComboBoxAction implements DumbAware { @Override public boolean isSelected(AnActionEvent e) { - return myBuilder.isSharedToTeamMembers(); + return myBuilder.isProjectLevel(); } @Override public void setSelected(AnActionEvent e, boolean state) { - myBuilder.setShareToTeamMembers(state); + myBuilder.setIsProjectLevel(state); } } diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/ManageButtonBuilder.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/ManageButtonBuilder.java index 246008f90705..a225041b3bd7 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/ManageButtonBuilder.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/ManageButtonBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,9 @@ package com.intellij.profile.codeInspection.ui.header; * @author Dmitry Batkovich */ public interface ManageButtonBuilder { + boolean isProjectLevel(); - boolean isSharedToTeamMembers(); - - void setShareToTeamMembers(boolean share); + void setIsProjectLevel(boolean share); void copy(); @@ -43,5 +42,4 @@ public interface ManageButtonBuilder { void export(); void doImport(); - } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/ArbitraryPlaceUrlReferenceProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/ArbitraryPlaceUrlReferenceProvider.java index efa996099eac..c7bf6d2175db 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/ArbitraryPlaceUrlReferenceProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/ArbitraryPlaceUrlReferenceProvider.java @@ -18,6 +18,7 @@ package com.intellij.psi.impl.source.resolve.reference; import com.intellij.openapi.paths.GlobalPathReferenceProvider; import com.intellij.openapi.paths.PathReferenceManager; import com.intellij.openapi.util.UserDataCache; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.IssueNavigationConfiguration; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; @@ -47,7 +48,8 @@ public class ArbitraryPlaceUrlReferenceProvider extends PsiReferenceProvider { List refs = null; GlobalPathReferenceProvider provider = myReferenceProvider.get(); - for (IssueNavigationConfiguration.LinkMatch link : navigationConfiguration.findIssueLinks(element.getText())) { + CharSequence commentText = StringUtil.newBombedCharSequence(element.getText(), 500); + for (IssueNavigationConfiguration.LinkMatch link : navigationConfiguration.findIssueLinks(commentText)) { if (refs == null) refs = new SmartList(); if (provider == null) { provider = (GlobalPathReferenceProvider)PathReferenceManager.getInstance().getGlobalWebPathReferenceProvider(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java index d3e9e8ec904f..954d0532cbc9 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java @@ -443,20 +443,22 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT oldFile.putUserData(FileContextUtil.INJECTED_IN_ELEMENT, injectedPsi.getUserData(FileContextUtil.INJECTED_IN_ELEMENT)); assert shreds.isValid(); - oldViewProvider.performNonPhysically(new Runnable() { - @Override - public void run() { - DebugUtil.startPsiModification("injected tree diff"); - try { - final DiffLog diffLog = BlockSupportImpl.mergeTrees(oldFile, oldFileNode, injectedNode, new DaemonProgressIndicator(), - oldFileNode.getText()); - DocumentCommitProcessor.doActualPsiChange(oldFile, diffLog); + if (!oldFile.textMatches(injectedPsi)) { + oldViewProvider.performNonPhysically(new Runnable() { + @Override + public void run() { + DebugUtil.startPsiModification("injected tree diff"); + try { + final DiffLog diffLog = BlockSupportImpl.mergeTrees(oldFile, oldFileNode, injectedNode, new DaemonProgressIndicator(), + oldFileNode.getText()); + DocumentCommitProcessor.doActualPsiChange(oldFile, diffLog); + } + finally { + DebugUtil.finishPsiModification(); + } } - finally { - DebugUtil.finishPsiModification(); - } - } - }); + }); + } assert shreds.isValid(); return oldFile; diff --git a/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/InplaceVariableIntroducer.java b/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/InplaceVariableIntroducer.java index ad90339189d4..d5a5aa91a649 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/InplaceVariableIntroducer.java +++ b/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/InplaceVariableIntroducer.java @@ -21,8 +21,8 @@ import com.intellij.codeInsight.template.ExpressionContext; import com.intellij.codeInsight.template.TextResult; import com.intellij.codeInsight.template.impl.TemplateManagerImpl; import com.intellij.codeInsight.template.impl.TemplateState; -import com.intellij.lang.ASTNode; -import com.intellij.lang.LanguageTokenSeparatorGenerators; +import com.intellij.lang.*; +import com.intellij.lexer.Lexer; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.command.impl.StartMarkAction; @@ -76,20 +76,24 @@ public abstract class InplaceVariableIntroducer extends In myOccurrences = occurrences; if (expr != null) { final ASTNode node = expr.getNode(); + ASTNode prev = node.getTreePrev(); final ASTNode astNode = LanguageTokenSeparatorGenerators.INSTANCE.forLanguage(expr.getLanguage()) - .generateWhitespaceBetweenTokens(node.getTreePrev(), node); + .generateWhitespaceBetweenTokens(prev, node); if (astNode != null) { - PostprocessReformattingAspect.getInstance(project).disablePostprocessFormattingInside(new Runnable() { - @Override - public void run() { - new WriteCommandAction(project, "Normalize declaration") { - @Override - protected void run(@NotNull Result result) throws Throwable { - node.getTreeParent().addChild(astNode, node); - } - }.execute(); - } - }); + final Lexer lexer = LanguageParserDefinitions.INSTANCE.forLanguage(expr.getLanguage()).createLexer(project); + if (LanguageUtil.canStickTokensTogetherByLexer(prev, prev, lexer) == ParserDefinition.SpaceRequirements.MUST) { + PostprocessReformattingAspect.getInstance(project).disablePostprocessFormattingInside(new Runnable() { + @Override + public void run() { + new WriteCommandAction(project, "Normalize declaration") { + @Override + protected void run(@NotNull Result result) throws Throwable { + node.getTreeParent().addChild(astNode, node); + } + }.execute(); + } + }); + } } myExpr = expr; } diff --git a/platform/lang-impl/src/com/intellij/refactoring/invertBoolean/InvertBooleanDelegate.java b/platform/lang-impl/src/com/intellij/refactoring/invertBoolean/InvertBooleanDelegate.java index f6cc85567dc3..5fdc05ff7175 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/invertBoolean/InvertBooleanDelegate.java +++ b/platform/lang-impl/src/com/intellij/refactoring/invertBoolean/InvertBooleanDelegate.java @@ -64,24 +64,24 @@ public abstract class InvertBooleanDelegate { /** * Eventually collect additional elements to rename, e.g. override methods - * and find expressions which need to be inverted + * and find expressions which need to be inverted, e.g. return method statements inside the method itself, etc * * @param renameProcessor null if element is not named or name was not changed */ public abstract void collectRefElements(PsiElement element, - Collection elementsToInvert, @Nullable RenameProcessor renameProcessor, - @NotNull String newName); + @NotNull String newName, + Collection elementsToInvert); /** - * Invoked from {@link #getForeignElementToInvert(com.intellij.psi.PsiElement, com.intellij.psi.PsiElement, com.intellij.lang.Language)}; + * Invoked from {@link #getForeignElementToInvert(PsiElement, PsiElement, Language)}; * should be used to reject usages for elements from foreign language to be refactored * @return null, if reference should not be reverted */ public abstract PsiElement getElementToInvert(PsiElement namedElement, PsiElement expression); /** - * Should be called from {@link #collectRefElements(PsiElement, Collection, RenameProcessor, String)} + * Should be called from {@link #collectRefElements(PsiElement, RenameProcessor, String, Collection)} * to process found usages in foreign languages */ protected static PsiElement getForeignElementToInvert(PsiElement namedElement, @@ -98,18 +98,18 @@ public abstract class InvertBooleanDelegate { /** * Replace expression with created negation - * @param expression to be inverted, found in {@link #collectRefElements(PsiElement, Collection, RenameProcessor, String)} + * @param expression to be inverted, found in {@link #collectRefElements(PsiElement, RenameProcessor, String, Collection)} */ public abstract void replaceWithNegatedExpression(PsiElement expression); /** - * Initialize variable with negated default initializer when default was initially omitted + * Initialize variable with negated default initializer when default was initially omitted, + * or invert variable initializer */ - public void invertDefaultElementInitializer(PsiElement var) {} + public abstract void invertElementInitializer(PsiElement var); /** * Detect usages which can't be inverted */ - public void findConflicts(MultiMap conflicts, - UsageInfo[] usageInfos) {} + public void findConflicts(UsageInfo[] usageInfos, MultiMap conflicts) {} } diff --git a/platform/lang-impl/src/com/intellij/refactoring/invertBoolean/InvertBooleanProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/invertBoolean/InvertBooleanProcessor.java index 1950f3fa015e..d2814b92a79e 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/invertBoolean/InvertBooleanProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/invertBoolean/InvertBooleanProcessor.java @@ -70,7 +70,7 @@ public class InvertBooleanProcessor extends BaseRefactoringProcessor { @Override protected boolean preprocessUsages(@NotNull Ref refUsages) { final MultiMap conflicts = new MultiMap(); - myDelegate.findConflicts(conflicts, refUsages.get()); + myDelegate.findConflicts(refUsages.get(), conflicts); if (!conflicts.isEmpty()) { return showConflicts(conflicts, null); @@ -89,7 +89,7 @@ public class InvertBooleanProcessor extends BaseRefactoringProcessor { final List toInvert = new ArrayList(); final LinkedHashSet elementsToInvert = new LinkedHashSet(); - myDelegate.collectRefElements(myElement, elementsToInvert, myRenameProcessor, myNewName); + myDelegate.collectRefElements(myElement, myRenameProcessor, myNewName, elementsToInvert); for (PsiElement element : elementsToInvert) { toInvert.add(mySmartPointerManager.createSmartPsiElementPointer(element)); } @@ -170,7 +170,7 @@ public class InvertBooleanProcessor extends BaseRefactoringProcessor { } } - myDelegate.invertDefaultElementInitializer(myElement); + myDelegate.invertElementInitializer(myElement); } @Override diff --git a/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java b/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java index d1bb5b0344ab..889926e7c7ad 100644 --- a/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java +++ b/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java @@ -45,6 +45,7 @@ import java.util.*; * Main idea of the class is to accept parameters "as-is", just as they should look to an external process, and quote/escape them * as required by the underlying platform. * + * @see com.intellij.execution.util.ExecUtil * @see com.intellij.execution.process.OSProcessHandler */ public class GeneralCommandLine implements UserDataHolder { @@ -146,7 +147,7 @@ public class GeneralCommandLine implements UserDataHolder { * @return unmodifiable map of the parent environment, that will be passed to the process if isPassParentEnvironment() == true */ @NotNull - public static Map getParentEnvironment() { + public Map getParentEnvironment() { return PlatformUtils.isAppCode() ? System.getenv() // Temporarily fix for OC-8606 : EnvironmentUtil.getEnvironmentMap(); } diff --git a/platform/platform-api/src/com/intellij/execution/configurations/PathEnvironmentVariableUtil.java b/platform/platform-api/src/com/intellij/execution/configurations/PathEnvironmentVariableUtil.java index bab90cfcd9b5..8ef1392c5c95 100644 --- a/platform/platform-api/src/com/intellij/execution/configurations/PathEnvironmentVariableUtil.java +++ b/platform/platform-api/src/com/intellij/execution/configurations/PathEnvironmentVariableUtil.java @@ -29,7 +29,7 @@ import java.util.Collections; import java.util.List; /** - * @author Sergey Simonchik + * A collection of utility methods for working with PATH environment variable. */ public class PathEnvironmentVariableUtil { @@ -43,7 +43,7 @@ public class PathEnvironmentVariableUtil { * listed in PATH environment variable. * * @param fileBaseName file base name - * @return {@code File} instance or null if not found + * @return {@link File} instance or null if not found */ @Nullable public static File findInPath(@NotNull String fileBaseName) { @@ -56,7 +56,7 @@ public class PathEnvironmentVariableUtil { * * @param fileBaseName file base name * @param filter exe file filter - * @return {@code File} instance or null if not found + * @return {@link File} instance or null if not found */ @Nullable public static File findInPath(@NotNull String fileBaseName, @Nullable FileFilter filter) { @@ -69,7 +69,7 @@ public class PathEnvironmentVariableUtil { * * @param fileBaseName file base name * @param logDetails true if extra logging is needed - * @return {@code File} instance or null if not found + * @return {@link File} instance or null if not found */ @Nullable public static File findInPath(@NotNull String fileBaseName, boolean logDetails) { @@ -88,7 +88,7 @@ public class PathEnvironmentVariableUtil { * Original PATH environment variable value is a value returned by System.getenv("PATH"). * * @param fileBaseName file base name - * @return {@code File} instance or null if not found + * @return {@link File} instance or null if not found */ private static File findInOriginalPath(@NotNull String fileBaseName) { String originalPath; @@ -188,5 +188,4 @@ public class PathEnvironmentVariableUtil { } return exePath; } - } diff --git a/platform/platform-api/src/com/intellij/execution/process/CapturingProcessAdapter.java b/platform/platform-api/src/com/intellij/execution/process/CapturingProcessAdapter.java index b3d4c16679ea..71a11c7bc81a 100644 --- a/platform/platform-api/src/com/intellij/execution/process/CapturingProcessAdapter.java +++ b/platform/platform-api/src/com/intellij/execution/process/CapturingProcessAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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. @@ -24,14 +24,14 @@ import org.jetbrains.annotations.NotNull; public class CapturingProcessAdapter extends ProcessAdapter { private final ProcessOutput myOutput; - public CapturingProcessAdapter(ProcessOutput output) { - myOutput = output; - } - public CapturingProcessAdapter() { this(new ProcessOutput()); } + public CapturingProcessAdapter(@NotNull ProcessOutput output) { + myOutput = output; + } + @Override public void onTextAvailable(ProcessEvent event, Key outputType) { addToOutput(event.getText(), outputType); @@ -41,13 +41,13 @@ public class CapturingProcessAdapter extends ProcessAdapter { if (outputType == ProcessOutputTypes.STDOUT) { myOutput.appendStdout(text); } - if (outputType == ProcessOutputTypes.STDERR) { + else if (outputType == ProcessOutputTypes.STDERR) { myOutput.appendStderr(text); } } @Override - public void processTerminated(@NotNull final ProcessEvent event) { + public void processTerminated(@NotNull ProcessEvent event) { myOutput.setExitCode(event.getExitCode()); } diff --git a/platform/platform-api/src/com/intellij/execution/process/CapturingProcessHandler.java b/platform/platform-api/src/com/intellij/execution/process/CapturingProcessHandler.java index c1a8d5b62c16..959f50291af6 100644 --- a/platform/platform-api/src/com/intellij/execution/process/CapturingProcessHandler.java +++ b/platform/platform-api/src/com/intellij/execution/process/CapturingProcessHandler.java @@ -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. @@ -18,10 +18,9 @@ package com.intellij.execution.process; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.nio.charset.Charset; @@ -32,22 +31,23 @@ import java.nio.charset.Charset; */ public class CapturingProcessHandler extends OSProcessHandler { private static final Logger LOG = Logger.getInstance(CapturingProcessHandler.class); + private final ProcessOutput myOutput = new ProcessOutput(); - + public CapturingProcessHandler(@NotNull GeneralCommandLine commandLine) throws ExecutionException { super(commandLine); addProcessListener(createProcessAdapter(myOutput)); } - public CapturingProcessHandler(final Process process) { + public CapturingProcessHandler(@NotNull Process process) { this(process, null, ""); } - public CapturingProcessHandler(final Process process, final Charset charset) { + public CapturingProcessHandler(@NotNull Process process, @Nullable Charset charset) { this(process, charset, ""); } - public CapturingProcessHandler(final Process process, final Charset charset, final String commandLine) { + public CapturingProcessHandler(@NotNull Process process, @Nullable Charset charset, @Nullable String commandLine) { super(process, commandLine, charset); addProcessListener(createProcessAdapter(myOutput)); } @@ -55,7 +55,7 @@ public class CapturingProcessHandler extends OSProcessHandler { protected CapturingProcessAdapter createProcessAdapter(ProcessOutput processOutput) { return new CapturingProcessAdapter(processOutput); } - + public ProcessOutput runProcess() { startNotify(); if (waitFor()) { @@ -103,10 +103,7 @@ public class CapturingProcessHandler extends OSProcessHandler { @Override public Charset getCharset() { - if (myCharset != null) { - return myCharset; - } - return super.getCharset(); + return myCharset != null ? myCharset : super.getCharset(); } @NotNull @@ -129,5 +126,4 @@ public class CapturingProcessHandler extends OSProcessHandler { } return myOutput; } - } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java b/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java index 652d83efa65d..05ad18289121 100644 --- a/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java +++ b/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java @@ -27,13 +27,14 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.ContainerUtilRt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.*; import java.nio.charset.Charset; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Map; public class ExecUtil { private static final NotNullLazyValue hasGkSudo = new NotNullLazyValue() { @@ -137,7 +138,7 @@ public class ExecUtil { @NotNull public static ProcessOutput execAndGetOutput(@NotNull GeneralCommandLine commandLine) throws ExecutionException { - return new CapturingProcessHandler(commandLine.createProcess()).runProcess(); + return new CapturingProcessHandler(commandLine).runProcess(); } @Nullable @@ -153,7 +154,6 @@ public class ExecUtil { @Nullable public static String readFirstLine(@NotNull InputStream stream, @Nullable Charset cs) { try { - @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") BufferedReader reader = new BufferedReader(cs == null ? new InputStreamReader(stream) : new InputStreamReader(stream, cs)); try { return reader.readLine(); @@ -178,66 +178,57 @@ public class ExecUtil { */ @NotNull public static Process sudo(@NotNull GeneralCommandLine commandLine, @NotNull String prompt) throws ExecutionException, IOException { - final File workDir = commandLine.getWorkDirectory(); - final List command = new ArrayList(); + return sudoCommand(commandLine, prompt).createProcess(); + } + + @NotNull + private static GeneralCommandLine sudoCommand(@NotNull GeneralCommandLine commandLine, @NotNull String prompt) throws ExecutionException, IOException { + if (SystemInfo.isUnix && "root".equals(System.getenv("USER"))) { + return commandLine; + } + + List command = ContainerUtil.newArrayList(); command.add(commandLine.getExePath()); command.addAll(commandLine.getParametersList().getList()); - final Map environment = commandLine.getEnvironment(); + GeneralCommandLine sudoCommandLine; if (SystemInfo.isMac) { - final String escapedCommandLine = StringUtil.join(command, new Function() { + String escapedCommandLine = StringUtil.join(command, new Function() { @Override public String fun(String s) { return escapeAppleScriptArgument(s); } }, " & \" \" & "); - final String escapedScript = "tell current application\n" + - " activate\n" + - " do shell script " + escapedCommandLine + " with administrator privileges without altering line endings\n" + - "end tell"; - return new GeneralCommandLine(getOsascriptPath(), "-e", escapedScript) - .withWorkDirectory(workDir) - .withEnvironment(environment) - .createProcess(); - } - else if ("root".equals(System.getenv("USER"))) { - return new GeneralCommandLine(command).withWorkDirectory(workDir).withEnvironment(environment).createProcess(); + String escapedScript = "tell current application\n" + + " activate\n" + + " do shell script " + escapedCommandLine + " with administrator privileges without altering line endings\n" + + "end tell"; + sudoCommandLine = new GeneralCommandLine(getOsascriptPath(), "-e", escapedScript); } else if (hasGkSudo.getValue()) { - final List sudoCommand = new ArrayList(); + List sudoCommand = ContainerUtil.newArrayList(); sudoCommand.addAll(Arrays.asList("gksudo", "--message", prompt, "--")); sudoCommand.addAll(command); - return new GeneralCommandLine(sudoCommand).withWorkDirectory(workDir).withEnvironment(environment).createProcess(); + sudoCommandLine = new GeneralCommandLine(sudoCommand); } else if (hasKdeSudo.getValue()) { - final List sudoCommand = new ArrayList(); + List sudoCommand = ContainerUtil.newArrayList(); sudoCommand.addAll(Arrays.asList("kdesudo", "--comment", prompt, "--")); sudoCommand.addAll(command); - return new GeneralCommandLine(sudoCommand).withWorkDirectory(workDir).withEnvironment(environment).createProcess(); + sudoCommandLine = new GeneralCommandLine(sudoCommand); } else if (hasPkExec.getValue()) { - final List sudoCommand = new ArrayList(); - sudoCommand.add("pkexec"); - sudoCommand.addAll(command); - - final Map parentEnvironment = GeneralCommandLine.getParentEnvironment(); - final Map inheritedEnvironment = ContainerUtilRt.newHashMap(parentEnvironment.size()); - - for (Map.Entry envVariable : parentEnvironment.entrySet()) { - inheritedEnvironment.put(envVariable.getKey(), escapeUnixShellArgument(envVariable.getValue())); - } - inheritedEnvironment.putAll(environment); - - return new GeneralCommandLine(sudoCommand).withWorkDirectory(workDir).withEnvironment(inheritedEnvironment).createProcess(); + command.add(0, "pkexec"); + sudoCommandLine = new GeneralCommandLine(command); } else if (SystemInfo.isUnix && hasTerminalApp()) { - final String escapedCommandLine = StringUtil.join(command, new Function() { + String escapedCommandLine = StringUtil.join(command, new Function() { @Override public String fun(String s) { return escapeUnixShellArgument(s); } }, " "); - final File script = createTempExecutableScript( + File script = createTempExecutableScript( "sudo", ".sh", "#!/bin/sh\n" + "echo " + escapeUnixShellArgument(prompt) + "\n" + @@ -247,20 +238,22 @@ public class ExecUtil { "echo\n" + "read -p \"Press Enter to close this window...\" TEMP\n" + "exit $STATUS\n"); - return new GeneralCommandLine(getTerminalCommand("Install", script.getAbsolutePath())) - .withWorkDirectory(workDir) - .withEnvironment(environment) - .createProcess(); + sudoCommandLine = new GeneralCommandLine(getTerminalCommand("Install", script.getAbsolutePath())); + } + else { + throw new UnsupportedSystemException(); } - throw new UnsupportedSystemException(); + return sudoCommandLine + .withWorkDirectory(commandLine.getWorkDirectory()) + .withEnvironment(commandLine.getEnvironment()) + .withPassParentEnvironment(commandLine.isPassParentEnvironment()) + .withRedirectErrorStream(commandLine.isRedirectErrorStream()); } @NotNull public static ProcessOutput sudoAndGetOutput(@NotNull GeneralCommandLine commandLine, @NotNull String prompt) throws IOException, ExecutionException { - final Process process = sudo(commandLine, prompt); - final CapturingProcessHandler processHandler = new CapturingProcessHandler(process); - return processHandler.runProcess(); + return execAndGetOutput(sudoCommand(commandLine, prompt)); } @NotNull diff --git a/platform/platform-api/src/com/intellij/ide/TextCopyProvider.java b/platform/platform-api/src/com/intellij/ide/TextCopyProvider.java new file mode 100644 index 000000000000..05c2d4bab6d9 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ide/TextCopyProvider.java @@ -0,0 +1,64 @@ +/* + * 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.ide; + +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.ide.CopyPasteManager; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.datatransfer.StringSelection; +import java.util.Collection; + +/** + * Base class for text-based copy provider + * + * @author Konstantin Bulenkov + */ +public abstract class TextCopyProvider implements CopyProvider { + /** + * Returns a collection of text blocks to be joined using {@link #getLinesSeparator()} separator. + * Returns null of empty collection if copy operation can not be performed + * + * @return collection of text blocks or null + */ + @Nullable + public abstract Collection getTextLinesToCopy(); + + @Override + public void performCopy(@NotNull DataContext dataContext) { + Collection lines = getTextLinesToCopy(); + if (lines != null && !lines.isEmpty()) { + String text = StringUtil.join(lines, getLinesSeparator()); + CopyPasteManager.getInstance().setContents(new StringSelection(text)); + } + } + + public String getLinesSeparator() { + return "\n"; + } + + @Override + public boolean isCopyEnabled(@NotNull DataContext dataContext) { + return getTextLinesToCopy() != null; + } + + @Override + public boolean isCopyVisible(@NotNull DataContext dataContext) { + return true; + } +} diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java index 07ba96c59abc..7f2c50e9e5ce 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java @@ -167,6 +167,8 @@ public interface IdeActions { @NonNls String ACTION_PREVIOUS_DIFF = "PreviousDiff"; @NonNls String ACTION_NEXT_DIFF = "NextDiff"; @NonNls String GROUP_DIFF_EDITOR_POPUP = "Diff.EditorPopupMenu"; + @NonNls String DIFF_VIEWER_POPUP = "Diff.ViewerPopupMenu"; + @NonNls String DIFF_VIEWER_TOOLBAR = "Diff.ViewerToolbar"; @NonNls String ACTION_EXPAND_ALL = "ExpandAll"; @NonNls String ACTION_COLLAPSE_ALL = "CollapseAll"; diff --git a/platform/platform-api/src/com/intellij/ui/ColoredListCellRenderer.java b/platform/platform-api/src/com/intellij/ui/ColoredListCellRenderer.java index 67ca238ce077..4a15ebed3bab 100644 --- a/platform/platform-api/src/com/intellij/ui/ColoredListCellRenderer.java +++ b/platform/platform-api/src/com/intellij/ui/ColoredListCellRenderer.java @@ -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. @@ -117,4 +117,14 @@ public abstract class ColoredListCellRenderer extends SimpleColoredComponent } protected abstract void customizeCellRenderer(JList list, T value, int index, boolean selected, boolean hasFocus); + + public abstract static class KotlinFriendlyColoredListCellRenderer extends ColoredListCellRenderer { + @Override + protected final void customizeCellRenderer(JList list, T value, int index, boolean selected, boolean hasFocus) { + + } + + // cannot specify type param in JList if JDK 6 + protected abstract void customizeCellRenderer(T value, int index, boolean selected, boolean hasFocus); + } } diff --git a/platform/platform-api/src/com/intellij/ui/components/JBList.java b/platform/platform-api/src/com/intellij/ui/components/JBList.java index 45e4a489e738..9f514d3c551f 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBList.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBList.java @@ -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. @@ -17,6 +17,7 @@ package com.intellij.ui.components; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.*; @@ -27,7 +28,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.plaf.UIResource; +import javax.swing.text.JTextComponent; import java.awt.*; +import java.awt.datatransfer.StringSelection; +import java.awt.event.ActionEvent; +import java.util.ArrayList; import java.util.Collection; /** @@ -158,6 +164,7 @@ public class JBList extends JList implements ComponentWithEmptyText, ComponentWi private void init() { setSelectionBackground(UIUtil.getListSelectionBackground()); setSelectionForeground(UIUtil.getListSelectionForeground()); + installDefaultCopyAction(); myEmptyText = new StatusText(this) { @Override @@ -170,6 +177,51 @@ public class JBList extends JList implements ComponentWithEmptyText, ComponentWi setCellRenderer(new DefaultListCellRenderer()); } + private void installDefaultCopyAction() { + final Action copy = getActionMap().get("copy"); + if (copy == null || copy instanceof UIResource) { + Action newCopy = new AbstractAction() { + @Override + public boolean isEnabled() { + return getSelectedIndex() != -1; + } + + @Override + public void actionPerformed(ActionEvent e) { + ArrayList selected = new ArrayList(); + JBList list = JBList.this; + ListCellRenderer renderer = list.getCellRenderer(); + if (renderer != null) { + for (int index : getSelectedIndices()) { + Object value = list.getModel().getElementAt(index); + //noinspection unchecked + Component c = renderer.getListCellRendererComponent(list, value, index, true, true); + SimpleColoredComponent coloredComponent = null; + if (c instanceof JComponent) { + coloredComponent = UIUtil.findComponentOfType((JComponent)c, SimpleColoredComponent.class); + } + if (coloredComponent != null) { + selected.add(coloredComponent.toString()); + } + else if (c instanceof JTextComponent) { + selected.add(((JTextComponent)c).getText()); + } + else if (value != null) { + selected.add(value.toString()); + } + } + } + + if (selected.size() > 0) { + String text = StringUtil.join(selected, " "); + CopyPasteManager.getInstance().setContents(new StringSelection(text)); + } + } + }; + getActionMap().put("copy", newCopy); + } + } + public boolean isEmpty() { return getItemsCount() == 0; } diff --git a/platform/platform-api/src/com/intellij/util/ui/tree/TreeUtil.java b/platform/platform-api/src/com/intellij/util/ui/tree/TreeUtil.java index d3e281010192..92348089686e 100644 --- a/platform/platform-api/src/com/intellij/util/ui/tree/TreeUtil.java +++ b/platform/platform-api/src/com/intellij/util/ui/tree/TreeUtil.java @@ -154,20 +154,12 @@ public final class TreeUtil { } @NotNull - public static TreePath getPath(final TreeNode aRootNode, @NotNull final TreeNode aNode) { - final List pathStack = new ArrayList(); - addEach(aRootNode, aNode, pathStack); - - final Object[] pathElements = new Object[pathStack.size()]; - - for (int i = pathStack.size() - 1; i >= 0; i--) { - pathElements[pathStack.size() - i - 1] = pathStack.get(i); - } - - return new TreePath(pathElements); + public static TreePath getPath(@NotNull TreeNode aRootNode, @NotNull TreeNode aNode) { + TreeNode[] nodes = getPathFromRootTo(aRootNode, aNode, true); + return new TreePath(nodes); } - public static boolean isAncestor(final TreeNode ancestor, final TreeNode node) { + public static boolean isAncestor(@NotNull TreeNode ancestor, @NotNull TreeNode node) { TreeNode parent = node; while (parent != null) { if (parent == ancestor) return true; @@ -192,21 +184,21 @@ public final class TreeUtil { @NotNull public static TreePath getPathFromRoot(@NotNull TreeNode node) { - TreeNode[] path = getPathFromRoot(node, 1); + TreeNode[] path = getPathFromRootTo(null, node, false); return new TreePath(path); } @NotNull - private static TreeNode[] getPathFromRoot(@NotNull TreeNode node, int depth) { - TreeNode[] path; - TreeNode parent = node.getParent(); - if (parent == null) { - path = new TreeNode[depth]; + private static TreeNode[] getPathFromRootTo(@Nullable TreeNode root, @NotNull TreeNode node, boolean includeRoot) { + int height = 0; + for (TreeNode n = node; n != root; n = n.getParent()) { + height++; } - else { - path = getPathFromRoot(parent, depth + 1); + TreeNode[] path = new TreeNode[includeRoot ? height+1 : height]; + int i = path.length-1; + for (TreeNode n = node; i>=0; n = n.getParent()) { + path[i--] = n; } - path[path.length - depth] = node; return path; } @@ -294,14 +286,6 @@ public final class TreeUtil { return selectionPath; } - private static void addEach(final TreeNode aRootNode, @NotNull final TreeNode aNode, @NotNull final List aPathStack) { - aPathStack.add(aNode); - - if (aNode != aRootNode) { - addEach(aRootNode, aNode.getParent(), aPathStack); - } - } - @NotNull private static IndexTreePathState removeLastPathComponent(@NotNull final DefaultTreeModel model, @NotNull final TreePath pathToBeRemoved) { final IndexTreePathState selectionState = new IndexTreePathState(pathToBeRemoved); @@ -343,11 +327,11 @@ public final class TreeUtil { return result.toArray(new TreePath[result.size()]); } - public static void sort(@NotNull final DefaultTreeModel model, final Comparator comparator) { + public static void sort(@NotNull final DefaultTreeModel model, @Nullable Comparator comparator) { sort((DefaultMutableTreeNode) model.getRoot(), comparator); } - public static void sort(@NotNull final DefaultMutableTreeNode node, final Comparator comparator) { + public static void sort(@NotNull final DefaultMutableTreeNode node, @Nullable Comparator comparator) { final List children = childrenToArray(node); Collections.sort(children, comparator); node.removeAllChildren(); diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java index 51ccdff43c77..b53c1a6cd250 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java @@ -521,6 +521,8 @@ public class IdeEventQueue extends EventQueue { } myEventCount++; + + traceClipboardEvents(e); if (e instanceof WindowEvent) { processAppActivationEvents((WindowEvent)e); @@ -648,6 +650,40 @@ public class IdeEventQueue extends EventQueue { } } + private static final Field ourInvocationEventRunnableAccessor; + + static { + Field field = null; + if (Registry.is("trace.clipboard.events") && SystemInfo.isJavaVersionAtLeast("1.8.0_60")) { + try { + field = InvocationEvent.class.getDeclaredField("runnable"); + field.setAccessible(true); + } + catch (Exception e) { + LOG.warn("Error creating accessor for java.awt.event.InvocationEvent.runnable field", e); + } + } + ourInvocationEventRunnableAccessor = field; + } + + private static void traceClipboardEvents(AWTEvent e) { + if (ourInvocationEventRunnableAccessor != null && e instanceof InvocationEvent && e.getClass().getName().equals("sun.awt.PeerEvent")) { + try { + Object r = ourInvocationEventRunnableAccessor.get(e); + if (r != null) { + String className = r.getClass().getName(); + // This check for event constructed in SunClipboard.lostOwnershipLater() (known to work with JDK 8u60) + if (className.contains("sun.awt.datatransfer.SunClipboard") && className.contains("Lambda")) { + LOG.info("Clipboard has been set by other application"); + } + } + } + catch (Exception ex) { + LOG.warn("Error accessing java.awt.event.InvocationEvent.runnable field"); + } + } + } + private MouseEvent lastClickEvent = null; private long lastClickTime = 0L; diff --git a/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java b/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileTypeAction.java similarity index 94% rename from platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java rename to platform/platform-impl/src/com/intellij/ide/actions/AssociateFileTypeAction.java index 8b1375d9f933..fdcb13d24fcf 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileTypeAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,12 +22,14 @@ import com.intellij.openapi.fileTypes.ex.FileTypeChooser; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -public class AssociateFileType extends AnAction { +public class AssociateFileTypeAction extends AnAction { + @Override public void actionPerformed(AnActionEvent e) { VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE); FileTypeChooser.associateFileType(file.getName()); } + @Override public void update(AnActionEvent e) { Presentation presentation = e.getPresentation(); DataContext dataContext = e.getDataContext(); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java b/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java index 0f6838683ffa..797ad3870360 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java @@ -17,12 +17,12 @@ package com.intellij.ide.actions; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.process.ProcessOutput; import com.intellij.execution.util.ExecUtil; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; @@ -46,9 +46,10 @@ import javax.swing.*; import java.io.File; import java.io.IOException; import java.util.Locale; +import java.util.Map; +import static com.intellij.openapi.util.Pair.pair; import static com.intellij.util.containers.ContainerUtil.newHashMap; -import static java.util.Arrays.asList; public class CreateDesktopEntryAction extends DumbAwareAction { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.CreateDesktopEntryAction"); @@ -58,72 +59,70 @@ public class CreateDesktopEntryAction extends DumbAwareAction { } @Override - public void update(final AnActionEvent event) { - final boolean enabled = isAvailable(); - final Presentation presentation = event.getPresentation(); - presentation.setEnabled(enabled); - presentation.setVisible(enabled); + public void update(@NotNull AnActionEvent event) { + boolean enabled = isAvailable(); + event.getPresentation().setEnabledAndVisible(enabled); } @Override - public void actionPerformed(final AnActionEvent event) { + public void actionPerformed(@NotNull AnActionEvent event) { if (!isAvailable()) return; - final Project project = event.getProject(); - final CreateDesktopEntryDialog dialog = new CreateDesktopEntryDialog(project); + Project project = event.getProject(); + CreateDesktopEntryDialog dialog = new CreateDesktopEntryDialog(project); if (!dialog.showAndGet()) { return; } final boolean globalEntry = dialog.myGlobalEntryCheckBox.isSelected(); - ProgressManager.getInstance().run(new Task.Backgroundable(project, event.getPresentation().getText()) { + ProgressManager.getInstance().run(new Task.Backgroundable(project, ApplicationBundle.message("desktop.entry.title")) { @Override - public void run(@NotNull final ProgressIndicator indicator) { + public void run(@NotNull ProgressIndicator indicator) { createDesktopEntry(getProject(), indicator, globalEntry); } }); } - public static void createDesktopEntry(@Nullable final Project project, - @NotNull final ProgressIndicator indicator, - final boolean globalEntry) { + public static void createDesktopEntry(@Nullable Project project, @NotNull ProgressIndicator indicator, boolean globalEntry) { if (!isAvailable()) return; - final double step = (1.0 - indicator.getFraction()) / 3.0; + double step = (1.0 - indicator.getFraction()) / 3.0; + File entry = null; try { indicator.setText(ApplicationBundle.message("desktop.entry.checking")); check(); indicator.setFraction(indicator.getFraction() + step); indicator.setText(ApplicationBundle.message("desktop.entry.preparing")); - final File entry = prepare(); + entry = prepare(); indicator.setFraction(indicator.getFraction() + step); indicator.setText(ApplicationBundle.message("desktop.entry.installing")); install(entry, globalEntry); indicator.setFraction(indicator.getFraction() + step); - final String message = ApplicationBundle.message("desktop.entry.success", - ApplicationNamesInfo.getInstance().getProductName()); if (ApplicationManager.getApplication() != null) { - Notifications.Bus - .notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Desktop entry created", message, NotificationType.INFORMATION)); + String message = ApplicationBundle.message("desktop.entry.success", ApplicationNamesInfo.getInstance().getProductName()); + Notifications.Bus.notify( + new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Desktop entry created", message, NotificationType.INFORMATION), + project); } } catch (Exception e) { if (ApplicationManager.getApplication() == null) { throw new RuntimeException(e); } - final String message = e.getMessage(); - if (!StringUtil.isEmptyOrSpaces(message)) { - LOG.warn(e); - Notifications.Bus.notify( - new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Failed to create desktop entry", message, NotificationType.ERROR), - project - ); - } - else { - LOG.error(e); + + LOG.warn(e); + String message = e.getMessage(); + if (StringUtil.isEmptyOrSpaces(message)) message = "Internal error"; + Notifications.Bus.notify( + new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Failed to create desktop entry", message, NotificationType.ERROR), + project); + } + finally { + if (entry != null) { + FileUtil.delete(entry); } } } @@ -134,71 +133,69 @@ public class CreateDesktopEntryAction extends DumbAwareAction { } private static File prepare() throws IOException { - final String homePath = PathManager.getHomePath(); + String homePath = PathManager.getHomePath(); assert new File(homePath).isDirectory() : "Invalid home path: '" + homePath + "'"; - final String binPath = homePath + "/bin"; + String binPath = homePath + "/bin"; assert new File(binPath).isDirectory() : "Invalid bin path: '" + binPath + "'"; String name = ApplicationNamesInfo.getInstance().getFullProductName(); if (PlatformUtils.isIdeaCommunity()) name += " Community Edition"; - final String iconPath = AppUIUtil.findIcon(binPath); + String iconPath = AppUIUtil.findIcon(binPath); if (iconPath == null) { throw new RuntimeException(ApplicationBundle.message("desktop.entry.icon.missing", binPath)); } - final String execPath = findScript(binPath); + String execPath = findScript(binPath); if (execPath == null) { throw new RuntimeException(ApplicationBundle.message("desktop.entry.script.missing", binPath)); } - final String wmClass = AppUIUtil.getFrameClass(); - - final String content = ExecUtil.loadTemplate(CreateDesktopEntryAction.class.getClassLoader(), "entry.desktop", - newHashMap(asList("$NAME$", "$SCRIPT$", "$ICON$", "$WM_CLASS$"), - asList(name, execPath, iconPath, wmClass))); - - final String entryName = wmClass + ".desktop"; - final File entryFile = new File(FileUtil.getTempDirectory(), entryName); + String wmClass = AppUIUtil.getFrameClass(); + Map vars = newHashMap(pair("$NAME$", name), pair("$SCRIPT$", execPath), pair("$ICON$", iconPath), pair("$WM_CLASS$", wmClass)); + String content = ExecUtil.loadTemplate(CreateDesktopEntryAction.class.getClassLoader(), "entry.desktop", vars); + File entryFile = new File(FileUtil.getTempDirectory(), wmClass + ".desktop"); FileUtil.writeToFile(entryFile, content); - entryFile.deleteOnExit(); return entryFile; } @Nullable private static String findScript(String binPath) { - String productName = ApplicationNamesInfo.getInstance().getProductName(); + ApplicationNamesInfo names = ApplicationNamesInfo.getInstance(); - String execPath = binPath + '/' + productName + ".sh"; + String execPath = binPath + '/' + names.getProductName() + ".sh"; if (new File(execPath).canExecute()) return execPath; - execPath = binPath + '/' + productName.toLowerCase(Locale.US) + ".sh"; + execPath = binPath + '/' + names.getProductName().toLowerCase(Locale.US) + ".sh"; if (new File(execPath).canExecute()) return execPath; - String scriptName = ApplicationNamesInfo.getInstance().getScriptName(); - execPath = binPath + '/' + scriptName + ".sh"; + execPath = binPath + '/' + names.getScriptName() + ".sh"; if (new File(execPath).canExecute()) return execPath; return null; } - private static void install(File entryFile, boolean globalEntry) throws IOException, ExecutionException, InterruptedException { + private static void install(File entryFile, boolean globalEntry) throws IOException, ExecutionException { if (globalEntry) { - File script = ExecUtil.createTempExecutableScript( - "sudo", ".sh", "#!/bin/sh\n" + - "xdg-desktop-menu install --mode system \"" + entryFile.getAbsolutePath() + "\"\n" + - "RV=$?\n" + - "xdg-desktop-menu forceupdate --mode system\n" + - "exit $RV\n"); - script.deleteOnExit(); String prompt = ApplicationBundle.message("desktop.entry.sudo.prompt"); - int result = ExecUtil.sudoAndGetOutput(new GeneralCommandLine(script.getPath()), prompt).getExitCode(); - if (result != 0) throw new RuntimeException("'" + script.getAbsolutePath() + "' : " + result); + exec(new GeneralCommandLine("xdg-desktop-menu", "install", "--mode", "system", entryFile.getAbsolutePath()), prompt); + exec(new GeneralCommandLine("xdg-desktop-menu", "forceupdate", "--mode", "system"), prompt); } else { - int result = new GeneralCommandLine("xdg-desktop-menu", "install", "--mode", "user", entryFile.getAbsolutePath()).createProcess().waitFor(); - if (result != 0) throw new RuntimeException("'" + entryFile.getAbsolutePath() + "' : " + result); - new GeneralCommandLine("xdg-desktop-menu", "forceupdate", "--mode", "user").createProcess().waitFor(); + exec(new GeneralCommandLine("xdg-desktop-menu", "install", "--mode", "user", entryFile.getAbsolutePath()), null); + exec(new GeneralCommandLine("xdg-desktop-menu", "forceupdate", "--mode", "user"), null); + } + } + + private static void exec(GeneralCommandLine command, @Nullable String prompt) throws IOException, ExecutionException { + command.setRedirectErrorStream(true); + ProcessOutput result = prompt != null ? ExecUtil.sudoAndGetOutput(command, prompt) : ExecUtil.execAndGetOutput(command); + int exitCode = result.getExitCode(); + if (exitCode != 0) { + String message = "Command '" + (prompt != null ? "sudo " : "") + command.getCommandLineString() + "' returned " + exitCode; + String output = result.getStdout(); + if (!StringUtil.isEmptyOrSpaces(output)) message += "\nOutput: " + output.trim(); + throw new RuntimeException(message); } } @@ -210,7 +207,7 @@ public class CreateDesktopEntryAction extends DumbAwareAction { public CreateDesktopEntryDialog(final Project project) { super(project); init(); - setTitle("Create Desktop Entry"); + setTitle(ApplicationBundle.message("desktop.entry.title")); myLabel.setText(myLabel.getText().replace("$APP_NAME$", ApplicationNamesInfo.getInstance().getProductName())); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ToggleDistractionFreeModeAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ToggleDistractionFreeModeAction.java index 72c4e8a6b21d..497f2e6a1dba 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ToggleDistractionFreeModeAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ToggleDistractionFreeModeAction.java @@ -95,7 +95,7 @@ public class ToggleDistractionFreeModeAction extends DumbAwareAction { p.setValue(before + "ARE_LINE_NUMBERS_SHOWN", valueOf(eo.ARE_LINE_NUMBERS_SHOWN)); eo.ARE_LINE_NUMBERS_SHOWN = p.getBoolean(after + "ARE_LINE_NUMBERS_SHOWN", value); p.setValue(before + "IS_RIGHT_MARGIN_SHOWN", valueOf(eo.IS_RIGHT_MARGIN_SHOWN)); eo.IS_RIGHT_MARGIN_SHOWN = p.getBoolean(after + "IS_RIGHT_MARGIN_SHOWN", value); p.setValue(before + "IS_INDENT_GUIDES_SHOWN", valueOf(eo.IS_INDENT_GUIDES_SHOWN)); eo.IS_INDENT_GUIDES_SHOWN = p.getBoolean(after + "IS_INDENT_GUIDES_SHOWN", value); - p.setValue(before + "ARE_BREADCRUMBS_SHOWN", valueOf(eo.ARE_BREADCRUMBS_SHOWN)); eo.ARE_BREADCRUMBS_SHOWN = p.getBoolean(after + "ARE_BREADCRUMBS_SHOWN", value); + p.setValue(before + "SHOW_BREADCRUMBS", valueOf(eo.SHOW_BREADCRUMBS)); eo.SHOW_BREADCRUMBS = p.getBoolean(after + "SHOW_BREADCRUMBS", value); p.setValue(before + "SHOW_METHOD_SEPARATORS", valueOf(ds.SHOW_METHOD_SEPARATORS)); ds.SHOW_METHOD_SEPARATORS = p.getBoolean(after + "SHOW_METHOD_SEPARATORS", value); diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form index 2f74192e7759..9432042bd455 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form +++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form @@ -3,7 +3,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -38,7 +38,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -129,7 +129,7 @@ - + @@ -147,7 +147,7 @@ - + @@ -156,7 +156,7 @@ - + @@ -168,7 +168,7 @@ - + @@ -194,7 +194,7 @@ - + @@ -203,7 +203,7 @@ - + @@ -228,7 +228,7 @@ - + diff --git a/platform/platform-impl/src/com/intellij/ide/ui/ColorBlindnessPanel.java b/platform/platform-impl/src/com/intellij/ide/ui/ColorBlindnessPanel.java index 36acab939e15..1b590106a31b 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/ColorBlindnessPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/ColorBlindnessPanel.java @@ -15,8 +15,8 @@ */ package com.intellij.ide.ui; -import com.intellij.ide.IdeBundle; import com.intellij.openapi.ui.ComboBox; +import com.intellij.ui.UIBundle; import com.intellij.ui.components.panels.HorizontalLayout; import com.intellij.util.ui.JBUI; @@ -37,10 +37,10 @@ final class ColorBlindnessPanel extends JPanel implements ChangeListener { add(HorizontalLayout.LEFT, myComboBox); myCheckBox.setSelected(false); myCheckBox.addChangeListener(this); - myCheckBox.setText(IdeBundle.message("checkbox.color.blindness")); + myCheckBox.setText(UIBundle.message("color.blindness.checkbox.text")); int count = 0; for (ColorBlindness blindness : ColorBlindness.values()) { - String name = IdeBundle.message("combobox.color.blindness." + blindness.name()); + String name = UIBundle.message(blindness.key); if (!name.isEmpty()) { myComboBox.addItem(new Item(blindness, name)); count++; diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/IntelliJLaf.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/IntelliJLaf.java index 01ad2b972238..a7099cb45088 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/IntelliJLaf.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/IntelliJLaf.java @@ -76,8 +76,12 @@ public class IntelliJLaf extends DarculaLaf { } public static boolean isGraphite() { - // https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSCell_Class/index.html#//apple_ref/doc/c_ref/NSGraphiteControlTint - // NSGraphiteControlTint = 6 - return Foundation.invoke("NSColor", "currentControlTint").intValue() == 6; + try { + // https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSCell_Class/index.html#//apple_ref/doc/c_ref/NSGraphiteControlTint + // NSGraphiteControlTint = 6 + return Foundation.invoke("NSColor", "currentControlTint").intValue() == 6; + } catch (Exception e) { + return false; + } } } diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxFocusedGraphite.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxFocusedGraphite.png new file mode 100644 index 000000000000..a0ad4fa4d35a Binary files /dev/null and b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxFocusedGraphite.png differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxFocusedGraphite@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxFocusedGraphite@2x.png new file mode 100644 index 000000000000..185902321ff4 Binary files /dev/null and b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxFocusedGraphite@2x.png differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxGraphite.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxGraphite.png new file mode 100644 index 000000000000..712c4891ffc8 Binary files /dev/null and b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxGraphite.png differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxGraphite@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxGraphite@2x.png new file mode 100644 index 000000000000..c54dd680be07 Binary files /dev/null and b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxGraphite@2x.png differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedFocusedGraphite.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedFocusedGraphite.png new file mode 100644 index 000000000000..4320dbc74018 Binary files /dev/null and b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedFocusedGraphite.png differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedFocusedGraphite@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedFocusedGraphite@2x.png new file mode 100644 index 000000000000..af6799c617e1 Binary files /dev/null and b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedFocusedGraphite@2x.png differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedGraphite.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedGraphite.png new file mode 100644 index 000000000000..d4d517d3ab1e Binary files /dev/null and b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedGraphite.png differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedGraphite@2x.png b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedGraphite@2x.png new file mode 100644 index 000000000000..479574144bdc Binary files /dev/null and b/platform/platform-impl/src/com/intellij/ide/ui/laf/icons/checkBoxSelectedGraphite@2x.png differ diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJCheckBoxUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJCheckBoxUI.java index 9f23d3505205..d36e84d36bcb 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJCheckBoxUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJCheckBoxUI.java @@ -15,7 +15,6 @@ */ package com.intellij.ide.ui.laf.intellij; -import com.intellij.ide.ui.laf.darcula.DarculaLaf; import com.intellij.util.ui.EmptyIcon; import com.intellij.util.ui.JBUI; @@ -30,10 +29,6 @@ import java.awt.*; */ public class MacIntelliJCheckBoxUI extends IntelliJCheckBoxUI { public static final Icon DEFAULT_ICON = EmptyIcon.create(20); - public static final Icon CHECKED_ICON = DarculaLaf.loadIcon("checkBoxSelected.png"); - public static final Icon CHECKED_ICON_FOCUSED = DarculaLaf.loadIcon("checkBoxSelectedFocused.png"); - public static final Icon UNCHECKED_ICON = DarculaLaf.loadIcon("checkBox.png"); - public static final Icon UNCHECKED_ICON_FOCUSED = DarculaLaf.loadIcon("checkBoxFocused.png"); public MacIntelliJCheckBoxUI(JCheckBox c) { c.setBorder(new MacCheckBoxBorder()); @@ -46,12 +41,9 @@ public class MacIntelliJCheckBoxUI extends IntelliJCheckBoxUI { @Override protected void drawCheckIcon(JComponent c, Graphics2D g, JCheckBox b, Rectangle iconRect, boolean selected, boolean enabled) { - getIcon(selected, enabled, c.hasFocus()).paintIcon(c, g, iconRect.x, iconRect.y); - } - - private Icon getIcon(boolean selected, boolean enabled, boolean focused) { - return focused ? selected ? CHECKED_ICON_FOCUSED : UNCHECKED_ICON_FOCUSED - : selected ? CHECKED_ICON : UNCHECKED_ICON; + MacIntelliJIconCache + .getIcon("checkBox", selected, c.hasFocus()) + .paintIcon(c, g, iconRect.x, iconRect.y); } @Override diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJIconCache.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJIconCache.java new file mode 100644 index 000000000000..c9cb59cbd286 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJIconCache.java @@ -0,0 +1,47 @@ +/* + * 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.ide.ui.laf.intellij; + +import com.intellij.ide.ui.laf.IntelliJLaf; +import com.intellij.openapi.util.IconLoader; +import com.intellij.util.containers.HashMap; + +import javax.swing.*; + +/** + * @author Konstantin Bulenkov + */ +public class MacIntelliJIconCache { + private static final HashMap cache = new HashMap(); + + public static Icon getIcon(String name, boolean selected, boolean focused, boolean disabled) { + String key = name; + if (selected) key+= "Selected"; + if (focused) key+= "Focused"; + else if (disabled) key+="Disabled"; + if (IntelliJLaf.isGraphite()) key+="Graphite"; + Icon icon = cache.get(key); + if (icon == null) { + icon = IconLoader.findIcon("/com/intellij/ide/ui/laf/icons/" + key + ".png", MacIntelliJIconCache.class, true); + cache.put(key, icon); + } + return icon; + } + + public static Icon getIcon(String name, boolean selected, boolean focused) { + return getIcon(name, selected, focused, false); + } +} diff --git a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java index a23a65cf7249..a87d02672d12 100644 --- a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java +++ b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java @@ -148,7 +148,7 @@ public class StartupUtil { return false; } } - + if (!"true".equals(System.getProperty("idea.no.64bit.check"))) { if (PlatformUtils.isCidr() && !SystemInfo.is64Bit) { String message = "32-bit JVM is not supported. Please install 64-bit version."; diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/StatisticsUploadAssistant.java b/platform/platform-impl/src/com/intellij/internal/statistic/StatisticsUploadAssistant.java index b417de0461fa..a5f21b4a0ec6 100755 --- a/platform/platform-impl/src/com/intellij/internal/statistic/StatisticsUploadAssistant.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/StatisticsUploadAssistant.java @@ -45,7 +45,7 @@ public class StatisticsUploadAssistant { return getData(Collections.emptySet()); } - public static boolean showNotification() { + public static boolean isShouldShowNotification() { return UsageStatisticsPersistenceComponent.getInstance().isShowNotification() && (System.currentTimeMillis() - Time.DAY > ((FeatureUsageTrackerImpl)FeatureUsageTracker.getInstance()).getFirstRunTime()); } diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java b/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java index 1d2691a715f7..249fe449508d 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,6 +46,12 @@ public class UsageTrigger implements PersistentStateComponent".getBytes(CharsetToolkit.UTF8_CHARSET); - - private static final Pair NON_EXISTENT_FILE_DATA = Pair.create(null, SystemProperties.getLineSeparator()); - private StorageUtil() { } public static void checkUnknownMacros(@NotNull final ComponentManager componentManager, @NotNull final Project project) { @@ -193,143 +176,6 @@ public class StorageUtil { } } - @NotNull - public static VirtualFile writeFile(@Nullable File file, - @NotNull Object requestor, - @Nullable VirtualFile virtualFile, - @NotNull Element element, - @NotNull LineSeparator lineSeparator, - boolean prependXmlProlog) throws IOException { - final VirtualFile result; - if (file != null && (virtualFile == null || !virtualFile.isValid())) { - result = getOrCreateVirtualFile(requestor, file); - } - else { - result = virtualFile; - assert result != null; - } - - if (LOG.isDebugEnabled() || ApplicationManager.getApplication().isUnitTestMode()) { - BufferExposingByteArrayOutputStream content = writeToBytes(element, lineSeparator.getSeparatorString()); - if (isEqualContent(result, lineSeparator, content)) { - throw new IllegalStateException("Content equals, but it must be handled not on this level: " + result.getName()); - } - else if (DEBUG_LOG != null && ApplicationManager.getApplication().isUnitTestMode()) { - DEBUG_LOG = result.getPath() + ":\n" + content + "\nOld Content:\n" + LoadTextUtil.loadText(result) + "\n---------"; - } - } - - doWrite(requestor, result, element, lineSeparator, prependXmlProlog); - return result; - } - - private static void doWrite(@NotNull final Object requestor, - @NotNull final VirtualFile file, - @NotNull Object content, - @NotNull final LineSeparator lineSeparator, - final boolean prependXmlProlog) throws IOException { - if (LOG.isDebugEnabled()) { - LOG.debug("Save " + file.getPresentableUrl()); - } - AccessToken token = WriteAction.start(); - try { - OutputStream out = file.getOutputStream(requestor); - try { - if (prependXmlProlog) { - out.write(XML_PROLOG); - out.write(lineSeparator.getSeparatorBytes()); - } - if (content instanceof Element) { - JDOMUtil.writeParent((Element)content, out, lineSeparator.getSeparatorString()); - } - else { - ((BufferExposingByteArrayOutputStream)content).writeTo(out); - } - } - finally { - out.close(); - } - } - catch (FileNotFoundException e) { - // may be element is not long-lived, so, we must write it to byte array - final BufferExposingByteArrayOutputStream byteArray = content instanceof Element ? writeToBytes((Element)content, lineSeparator.getSeparatorString()) : ((BufferExposingByteArrayOutputStream)content); - throw new ReadOnlyModificationException(file, e, new StateStorage.SaveSession() { - @Override - public void save() throws IOException { - doWrite(requestor, file, byteArray, lineSeparator, prependXmlProlog); - } - }); - } - finally { - token.finish(); - } - } - - private static boolean isEqualContent(@NotNull VirtualFile result, - @Nullable LineSeparator lineSeparatorIfPrependXmlProlog, - @NotNull BufferExposingByteArrayOutputStream content) throws IOException { - int headerLength = lineSeparatorIfPrependXmlProlog == null ? 0 : XML_PROLOG.length + lineSeparatorIfPrependXmlProlog.getSeparatorBytes().length; - if (result.getLength() != (headerLength + content.size())) { - return false; - } - - byte[] oldContent = result.contentsToByteArray(); - - if (lineSeparatorIfPrependXmlProlog != null && - (!ArrayUtil.startsWith(oldContent, XML_PROLOG) || !ArrayUtil.startsWith(oldContent, XML_PROLOG.length, lineSeparatorIfPrependXmlProlog.getSeparatorBytes()))) { - return false; - } - - for (int i = headerLength; i < oldContent.length; i++) { - if (oldContent[i] != content.getInternalBuffer()[i - headerLength]) { - return false; - } - } - return true; - } - - public static void deleteFile(@NotNull File file, @NotNull final Object requestor, @Nullable final VirtualFile virtualFile) throws IOException { - if (virtualFile == null) { - LOG.warn("Cannot find virtual file " + file.getAbsolutePath()); - } - - if (virtualFile == null) { - if (file.exists()) { - FileUtil.delete(file); - } - } - else if (virtualFile.exists()) { - try { - deleteFile(requestor, virtualFile); - } - catch (FileNotFoundException e) { - throw new ReadOnlyModificationException(virtualFile, e, new StateStorage.SaveSession() { - @Override - public void save() throws IOException { - deleteFile(requestor, virtualFile); - } - }); - } - } - } - - public static void deleteFile(@NotNull Object requestor, @NotNull VirtualFile virtualFile) throws IOException { - AccessToken token = WriteAction.start(); - try { - virtualFile.delete(requestor); - } - finally { - token.finish(); - } - } - - @NotNull - public static BufferExposingByteArrayOutputStream writeToBytes(@NotNull Parent element, @NotNull String lineSeparator) throws IOException { - BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(512); - JDOMUtil.writeParent(element, out, lineSeparator); - return out; - } - @NotNull public static VirtualFile getOrCreateVirtualFile(@Nullable final Object requestor, @NotNull final File file) throws IOException { VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); @@ -358,68 +204,4 @@ public class StorageUtil { token.finish(); } } - - @NotNull - public static VirtualFile createDir(@NotNull File ioDir, @NotNull Object requestor) throws IOException { - //noinspection ResultOfMethodCallIgnored - ioDir.mkdirs(); - String parentFile = ioDir.getParent(); - VirtualFile parentVirtualFile = parentFile == null ? null : VfsUtil.createDirectoryIfMissing(parentFile); - if (parentVirtualFile == null) { - throw new IOException(ProjectBundle.message("project.configuration.save.file.not.found", parentFile)); - } - return getFile(ioDir.getName(), parentVirtualFile, requestor); - } - - /** - * @return pair.first - file contents (null if file does not exist), pair.second - file line separators - */ - @NotNull - public static Pair loadFile(@Nullable final VirtualFile file) throws IOException { - if (file == null || !file.exists()) { - return NON_EXISTENT_FILE_DATA; - } - - byte[] bytes = file.contentsToByteArray(); - String lineSeparator = file.getDetectedLineSeparator(); - if (lineSeparator == null) { - lineSeparator = detectLineSeparators(CharsetToolkit.UTF8_CHARSET.decode(ByteBuffer.wrap(bytes)), null).getSeparatorString(); - } - return Pair.create(bytes, lineSeparator); - } - - @NotNull - public static LineSeparator detectLineSeparators(@NotNull CharSequence chars, @Nullable LineSeparator defaultSeparator) { - for (int i = 0, n = chars.length(); i < n; i++) { - char c = chars.charAt(i); - if (c == '\r') { - return LineSeparator.CRLF; - } - else if (c == '\n') { - // if we are here, there was no \r before - return LineSeparator.LF; - } - } - return defaultSeparator == null ? LineSeparator.getSystemLineSeparator() : defaultSeparator; - } - - public static boolean isProjectOrModuleFile(@NotNull String fileSpec) { - return StoragePathMacros.PROJECT_FILE.equals(fileSpec) || fileSpec.startsWith(StoragePathMacros.PROJECT_CONFIG_DIR) || fileSpec.equals(StoragePathMacros.MODULE_FILE); - } - - @NotNull - public static VirtualFile getFile(@NotNull String fileName, @NotNull VirtualFile parent, @NotNull Object requestor) throws IOException { - VirtualFile file = parent.findChild(fileName); - if (file != null) { - return file; - } - - AccessToken token = WriteAction.start(); - try { - return parent.createChildData(requestor, fileName); - } - finally { - token.finish(); - } - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java index b416e2b4683d..53c00623ea88 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java @@ -52,7 +52,7 @@ public class EditorSettingsExternalizable implements PersistentStateComponent= selectionAttributesIndex) { selectionBackgroundIsPotentiallyVisible = true; } - back = ifDiffers(attrs.getBackgroundColor(), myDefaultBackground); + back = attrs.getBackgroundColor(); } if (fontType == Font.PLAIN) { @@ -541,11 +540,6 @@ public final class IterationState { } } - @Nullable - private static Color ifDiffers(final Color c1, final Color c2) { - return Comparing.equal(c1, c2) ? null : c1; - } - public boolean atEnd() { return myStartOffset >= myEnd; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/IterationState.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/IterationState.java index 97dd5150dfb6..bc74c684fb90 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/IterationState.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/IterationState.java @@ -550,11 +550,11 @@ public class IterationState { TextAttributes attrs = cachedAttributes.get(i); if (fore == null) { - fore = ifDiffers(attrs.getForegroundColor(), myDefaultForeground); + fore = attrs.getForegroundColor(); } if (back == null) { - back = ifDiffers(attrs.getBackgroundColor(), myDefaultBackground); + back = attrs.getBackgroundColor(); } if (fontType == Font.PLAIN) { @@ -582,11 +582,6 @@ public class IterationState { includeLineStart && myStartOffset == myCaretRowStart || includeLineEnd && myStartOffset == myCaretRowEnd; } - @Nullable - private static Color ifDiffers(final Color c1, final Color c2) { - return Comparing.equal(c1, c2) ? null : c1; - } - public boolean atEnd() { return myReverseIteration ? myStartOffset <= myEnd : myStartOffset >= myEnd; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java index 83fa8dc423bf..f5936bd40e39 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java @@ -35,6 +35,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.ByteSequence; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtilRt; import com.intellij.openapi.vfs.*; @@ -62,6 +63,7 @@ import org.jetbrains.annotations.TestOnly; import java.io.*; import java.net.URL; +import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -79,13 +81,20 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent private static final int VERSION = 15; private static final Key FILE_TYPE_KEY = Key.create("FILE_TYPE_KEY"); // cached auto-detected file type. If the file was auto-detected as plain text or binary - // then the value is null and autoDetectedAsText, autoDetectedAsBinary and autoDetectWasRun sets are used instead. + // then the value is null and AUTO_DETECTED_* flags stored in packedFlags are used instead. private static final Key DETECTED_FROM_CONTENT_FILE_TYPE_KEY = Key.create("DETECTED_FROM_CONTENT_FILE_TYPE_KEY"); private static final int DETECT_BUFFER_SIZE = 8192; // the number of bytes to read from the file to feed to the file type detector // must be sorted - private static final String DEFAULT_IGNORED = - "*.hprof;*.pyc;*.pyo;*.rbc;*~;.DS_Store;.git;.hg;.svn;.tox;CVS;RCS;SCCS;__pycache__;_svn;rcs;vssver.scc;vssver2.scc;"; + private static final String DEFAULT_IGNORED = "*.hprof;*.pyc;*.pyo;*.rbc;*~;.DS_Store;.git;.hg;.svn;.tox;CVS;RCS;SCCS;__pycache__;_svn;rcs;vssver.scc;vssver2.scc;"; + static { + List strings = StringUtil.split(DEFAULT_IGNORED, ";"); + for (int i = 0; i < strings.size(); i++) { + String string = strings.get(i); + String prev = i==0?"":strings.get(i-1); + assert prev.compareTo(string) < 0 : "DEFAULT_IGNORED must be sorted, but got: '"+prev+"' >= '"+string+"'"; + } + } private static boolean RE_DETECT_ASYNC = !ApplicationManager.getApplication().isUnitTestMode(); private final Set myDefaultTypes = new THashSet(); @@ -128,10 +137,14 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent static final String FILE_SPEC = "filetypes"; // these flags are stored in 'packedFlags' as chunks of four bits - private static final int AUTO_DETECTED_AS_TEXT_MASK = 1; // set if the file was auto-detected as text - private static final int AUTO_DETECTED_AS_BINARY_MASK = 2; // set if the file was auto-detected as binary - private static final int AUTO_DETECT_WAS_RUN_MASK = 4; // set if auto-detection was performed for this file - private static final int ATTRIBUTES_WERE_LOADED_MASK = 8; // set if AUTO_* bits above were loaded from the file persistent attributes + private static final byte AUTO_DETECTED_AS_TEXT_MASK = 1<<0; // set if the file was auto-detected as text + private static final byte AUTO_DETECTED_AS_BINARY_MASK = 1<<1; // set if the file was auto-detected as binary + + // set if auto-detection was performed for this file. + // if some detector returned some custom file type, it's stored in DETECTED_FROM_CONTENT_FILE_TYPE_KEY file key. + // otherwise if auto-detected as text or binary, the result is stored in AUTO_DETECTED_AS_TEXT_MASK|AUTO_DETECTED_AS_BINARY_MASK bits + private static final byte AUTO_DETECT_WAS_RUN_MASK = 1<<2; + private static final byte ATTRIBUTES_WERE_LOADED_MASK = 1<<3; // set if AUTO_* bits above were loaded from the file persistent attributes and saved to packedFlags private final ConcurrentPackedBitsArray packedFlags = new ConcurrentPackedBitsArray(4); private final AtomicInteger counterAutoDetect = new AtomicInteger(); @@ -206,7 +219,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent VirtualFile file = event instanceof VFileCreateEvent ? /* avoid expensive find child here */ null : event.getFile(); VirtualFile filtered = file != null && wasAutoDetectedBefore(file) && isDetectable(file) ? file : null; if (toLog()) { - log("F: handled " + event + "; filtered file: " + filtered + "(file: "+file+"; wasAutoDetectedBefore(file): "+(file == null ? null : wasAutoDetectedBefore(file))+"; isDetectable(file): "+(file == null ? null : isDetectable(file))+"; file.getLength(): "+(file == null ? null : file.getLength())+"; file.isValid(): "+(file == null ? null : file.isValid())+"; file.is(VFileProperty.SPECIAL): "+(file == null ? null : file.is(VFileProperty.SPECIAL))+"; packedFlags.get(id): "+(!(file instanceof VirtualFileWithId) ? null : packedFlags.get(((VirtualFileWithId)file).getId()))+"; file.getFileSystem():"+(file == null ? null : file.getFileSystem())+")"); + log("F: handled " + event + "; filtered file: " + filtered + " (file: "+file+"; wasAutoDetectedBefore(file): "+(file == null ? null : wasAutoDetectedBefore(file))+"; isDetectable(file): "+(file == null ? null : isDetectable(file))+"; file.getLength(): "+(file == null ? null : file.getLength())+"; file.isValid(): "+(file == null ? null : file.isValid())+"; file.is(VFileProperty.SPECIAL): "+(file == null ? null : file.is(VFileProperty.SPECIAL))+"; packedFlags.get(id): "+(!(file instanceof VirtualFileWithId) ? null : packedFlags.get(((VirtualFileWithId)file).getId()))+"; file.getFileSystem():"+(file == null ? null : file.getFileSystem())+")"); } return filtered; } @@ -310,7 +323,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent return toLog; } - private static void log(@SuppressWarnings("UnusedParameters") String message) { + private static void log(String message) { System.out.println(message); } @@ -346,13 +359,26 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent log("F: Redetect file: " + file.getName() + "; shouldRedetect: " + shouldRedetect); } if (shouldRedetect) { - int id = file instanceof VirtualFileWithId ? ((VirtualFileWithId)file).getId() : -1; - FileType before = getAutoDetectedType(file, id); + int id = ((VirtualFileWithId)file).getId(); + long flags = packedFlags.get(id); + FileType before = fromCachedFlags(file, flags); - packedFlags.set(id, ATTRIBUTES_WERE_LOADED_MASK); + if (toLog()) { + log("F: reDetect("+file.getName()+") prepare to redetect. flags: "+flags); + } - file.putUserData(DETECTED_FROM_CONTENT_FILE_TYPE_KEY, null); - FileType after = getFileTypeByFile(file); // may be back to standard file type + FileType after = getOrDetectByFile(file); + + if (after == null) { + after = detectFromContentAndCache(file); + } + else { + // back to standard file type + // detected by conventional methods, no need to run detect-from-content + file.putUserData(DETECTED_FROM_CONTENT_FILE_TYPE_KEY, null); + flags = 0; + packedFlags.set(id, flags); + } if (toLog()) { log("F: After redetect file: " + file.getName() + "; before: " + before.getName() + "; after: " + after.getName()+"; now getFileType()="+file.getFileType().getName()); } @@ -437,6 +463,17 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent @Override @NotNull public FileType getFileTypeByFile(@NotNull VirtualFile file) { + FileType fileType = getOrDetectByFile(file); + + if (fileType == null) { + fileType = file instanceof StubVirtualFile ? UnknownFileType.INSTANCE : getOrDetectFromContent(file); + } + + return fileType; + } + + @Nullable // null means all conventional detect methods returned UnknownFileType.INSTANCE, have to detect from content + private FileType getOrDetectByFile(@NotNull VirtualFile file) { FileType fileType = file.getUserData(FILE_TYPE_KEY); if (fileType != null) return fileType; @@ -461,12 +498,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } return fileType; } - - if (!(file instanceof StubVirtualFile)) { - fileType = getOrDetectFromContent(file); - } - - return fileType; + return null; } @NotNull @@ -477,29 +509,28 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent if (id < 0) return UnknownFileType.INSTANCE; long flags = packedFlags.get(id); - boolean autoDetectWasRun = (flags & AUTO_DETECT_WAS_RUN_MASK) != 0; - if (autoDetectWasRun) { - FileType type = getAutoDetectedType(file, id); - if (toLog()) { - log("F: autodetected getFileType("+file.getName()+") = "+type.getName()); - } - return type; - } if ((flags & ATTRIBUTES_WERE_LOADED_MASK) == 0) { flags = readFlagsFromCache(file); + flags = BitUtil.set(flags, ATTRIBUTES_WERE_LOADED_MASK, true); + packedFlags.set(id, flags); + if (toLog()) { + log("F: readFlagsFromCache("+file.getName()+") = "+flags); + } } - boolean wasDetectedAsText = BitUtil.isSet(flags, AUTO_DETECTED_AS_TEXT_MASK); - boolean wasDetectedAsBinary = BitUtil.isSet(flags, AUTO_DETECTED_AS_BINARY_MASK); - boolean wasAutoDetectRun = BitUtil.isSet(flags, AUTO_DETECT_WAS_RUN_MASK); - if (wasAutoDetectRun && (wasDetectedAsText || wasDetectedAsBinary)) { - return wasDetectedAsText ? FileTypes.PLAIN_TEXT : UnknownFileType.INSTANCE; + boolean autoDetectWasRun = (flags & AUTO_DETECT_WAS_RUN_MASK) != 0; + if (autoDetectWasRun) { + FileType type = fromCachedFlags(file, flags); + if (toLog()) { + log("F: autodetected getFileType("+file.getName()+") = "+type.getName()+"; packedFlags.get(id):"+flags+"; DETECTED_FROM_CONTENT_FILE_TYPE_KEY: "+file.getUserData(DETECTED_FROM_CONTENT_FILE_TYPE_KEY)); + } + return type; } } FileType fileType = file.getUserData(DETECTED_FROM_CONTENT_FILE_TYPE_KEY); // run autodetection if (fileType == null) { - fileType = detectFromContent(file); + fileType = detectFromContentAndCache(file); } if (toLog()) { @@ -510,6 +541,8 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } private volatile FileAttribute autoDetectedAttribute; + // read auto-detection flags from the persistent FS file attributes. If file attributes are absent, return 0 for flags + // returns three bits value for AUTO_DETECTED_AS_TEXT_MASK, AUTO_DETECTED_AS_BINARY_MASK and AUTO_DETECT_WAS_RUN_MASK bits protected byte readFlagsFromCache(@NotNull VirtualFile file) { DataInputStream stream = autoDetectedAttribute.readAttribute(file); boolean wasAutoDetectRun = false; @@ -527,17 +560,18 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } catch (IOException ignored) { } - status = (byte)BitUtil.set(status, AUTO_DETECT_WAS_RUN_MASK, wasAutoDetectRun); - status = (byte)BitUtil.set(status, ATTRIBUTES_WERE_LOADED_MASK, true); + status = BitUtil.set(status, AUTO_DETECT_WAS_RUN_MASK, wasAutoDetectRun); - return status; + return (byte)(status & (AUTO_DETECTED_AS_TEXT_MASK | AUTO_DETECTED_AS_BINARY_MASK | AUTO_DETECT_WAS_RUN_MASK)); } + // store auto-detection flags to the persistent FS file attributes + // writes AUTO_DETECTED_AS_TEXT_MASK, AUTO_DETECTED_AS_BINARY_MASK bits only protected void writeFlagsToCache(@NotNull VirtualFile file, int flags) { DataOutputStream stream = autoDetectedAttribute.writeAttribute(file); try { try { - stream.writeByte(flags); + stream.writeByte(flags & (AUTO_DETECTED_AS_TEXT_MASK | AUTO_DETECTED_AS_BINARY_MASK)); } finally { stream.close(); @@ -553,11 +587,13 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent autoDetectedAttribute = autoDetectedAttribute.newVersion(count); PropertiesComponent.getInstance().setValue("fileTypeChangedCounter", Integer.toString(count)); packedFlags.clear(); + if (toLog()) { + log("F: clearCaches"); + } } @NotNull - private FileType getAutoDetectedType(@NotNull VirtualFile file, int id) { - long flags = packedFlags.get(id); + private static FileType fromCachedFlags(@NotNull VirtualFile file, long flags) { return BitUtil.isSet(flags, AUTO_DETECTED_AS_TEXT_MASK) ? FileTypes.PLAIN_TEXT : BitUtil.isSet(flags, AUTO_DETECTED_AS_BINARY_MASK) ? UnknownFileType.INSTANCE : ObjectUtils.notNull(file.getUserData(DETECTED_FROM_CONTENT_FILE_TYPE_KEY), FileTypes.PLAIN_TEXT); @@ -579,10 +615,12 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent writeFlagsToCache(file, flags); if (file instanceof VirtualFileWithId) { int id = Math.abs(((VirtualFileWithId)file).getId()); - flags = AUTO_DETECT_WAS_RUN_MASK | ATTRIBUTES_WERE_LOADED_MASK; - flags = BitUtil.set(flags, AUTO_DETECTED_AS_TEXT_MASK, wasAutodetectedAsText); - flags = BitUtil.set(flags, AUTO_DETECTED_AS_BINARY_MASK, wasAutodetectedAsBinary); + flags = BitUtil.set(flags, AUTO_DETECT_WAS_RUN_MASK, true); + flags = BitUtil.set(flags, ATTRIBUTES_WERE_LOADED_MASK, true); packedFlags.set(id, flags); + if (toLog()) { + log("F: cacheAutoDetectedFileType("+file.getName()+") = "+flags); + } if (wasAutodetectedAsText || wasAutodetectedAsBinary) { file.putUserData(DETECTED_FROM_CONTENT_FILE_TYPE_KEY, null); @@ -615,15 +653,37 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent return file.getFileSystem() instanceof FileSystemInterface && !SingleRootFileViewProvider.isTooLargeForContentLoading(file); } + private static boolean processFirstBytes(@NotNull final InputStream stream, final int length, @NotNull Processor processor) throws IOException { + final byte[] bytes = FileUtilRt.getThreadLocalBuffer(); + assert bytes.length >= length : "Cannot process more than " + bytes.length + " in one call, requested:" + length; + + int n = stream.read(bytes, 0, length); + if (n <= 0) { + // maybe locked because someone else is writing to it + // repeat inside read action to guarantee all writes are finished + n = ApplicationManager.getApplication().runReadAction(new ThrowableComputable() { + @Override + public Integer compute() throws IOException { + return stream.read(bytes, 0, length); + } + }); + if (n <= 0) { + return false; + } + } + + return processor.process(new ByteSequence(bytes, 0, n)); + } + @NotNull - private FileType detectFromContent(@NotNull final VirtualFile file) { + private FileType detectFromContentAndCache(@NotNull final VirtualFile file) { long start = System.currentTimeMillis(); try { final InputStream inputStream = ((FileSystemInterface)file.getFileSystem()).getInputStream(file); final Ref result = new Ref(UnknownFileType.INSTANCE); - boolean r; + boolean r = false; try { - r = FileUtil.processFirstBytes(inputStream, DETECT_BUFFER_SIZE, new Processor() { + r = processFirstBytes(inputStream, DETECT_BUFFER_SIZE, new Processor() { @Override public boolean process(ByteSequence byteSequence) { boolean isText = guessIfText(file, byteSequence); @@ -660,23 +720,23 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent }); } finally { + if (toLog()) { + byte[] buffer = new byte[50]; + InputStream newStream = ((FileSystemInterface)file.getFileSystem()).getInputStream(file); + int n = newStream.read(buffer, 0, buffer.length); + log("F: Redetect run for file: " + file.getName() + "; result: "+result.get().getName()+"; processor ret: "+r+"; stream: "+streamInfo(inputStream)+"; newStream: "+streamInfo(newStream)+"; read: "+n+"; buffer: "+Arrays.toString(buffer)); + newStream.close(); + try { + InputStream in = ReflectionUtil.getField(newStream.getClass(), newStream, InputStream.class, "in"); + String path = ReflectionUtil.getField(in.getClass(), in, String.class, "path"); + log("F: inputStream.in: " + in + "("+in.getClass()+"; in.path: "+path+"; path.exists "+new File(path).exists()+"; canRead: "+new File(path).canRead()); + } + catch (Exception ignored) { + } + } inputStream.close(); } FileType fileType = result.get(); - if (toLog()) { - byte[] buffer = new byte[50]; - InputStream newStream = ((FileSystemInterface)file.getFileSystem()).getInputStream(file); - int n = newStream.read(buffer, 0, buffer.length); - newStream.close(); - log("F: Redetect run for file: " + file.getName() + "; result: "+fileType.getName()+"; processor ret: "+r+"; stream: "+inputStream+"; newStream: "+newStream+"; read: "+n+"; buffer: "+Arrays.toString(buffer)); - try { - InputStream in = ReflectionUtil.getField(newStream.getClass(), newStream, InputStream.class, "in"); - String path = ReflectionUtil.getField(in.getClass(), in, String.class, "path"); - log("F: inputStream.in: " + in + "("+in.getClass()+"; in.path: "+path+"; path.exists "+new File(path).exists()+"; canRead: "+new File(path).canRead()); - } - catch (Exception ignored) { - } - } if (LOG.isDebugEnabled()) { LOG.debug(file + "; type=" + fileType.getDescription() + "; " + counterAutoDetect); @@ -694,6 +754,27 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } } + // for diagnostics + private static Object streamInfo(InputStream stream) throws IOException { + if (stream instanceof BufferedInputStream) { + InputStream in = ReflectionUtil.getField(stream.getClass(), stream, InputStream.class, "in"); + byte[] buf = ReflectionUtil.getField(stream.getClass(), stream, byte[].class, "buf"); + int count = ReflectionUtil.getField(stream.getClass(), stream, int.class, "count"); + int pos = ReflectionUtil.getField(stream.getClass(), stream, int.class, "pos"); + + return "BufferedInputStream(buf="+(buf == null ? null : Arrays.toString(Arrays.copyOf(buf, count))) + ", count="+count+ ", pos="+pos+", in="+streamInfo(in)+")"; + } + if (stream instanceof FileInputStream) { + String path = ReflectionUtil.getField(stream.getClass(), stream, String.class, "path"); + FileChannel channel = ReflectionUtil.getField(stream.getClass(), stream, FileChannel.class, "channel"); + boolean closed = ReflectionUtil.getField(stream.getClass(), stream, boolean.class, "closed"); + int available = stream.available(); + File file = new File(path); + return "FileInputStream(path="+path+ ", available="+available+ ", closed="+closed+ ", channel="+channel+", channel.size="+(channel==null?null:channel.size())+", file.exists=" + file.exists()+", file.content='"+FileUtil.loadFile(file)+"')"; + } + return stream; + } + private static boolean guessIfText(@NotNull VirtualFile file, @NotNull ByteSequence byteSequence) { byte[] bytes = byteSequence.getBytes(); Trinity guessed = LoadTextUtil.guessFromContent(file, bytes, byteSequence.getLength()); @@ -714,7 +795,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent return ((FileTypeIdentifiableByVirtualFile)type).isMyFileType(file); } - return getFileTypeByFileName(file.getName()) == type; + return getFileTypeByFileName(file.getNameSequence()) == type; } @Override @@ -1003,7 +1084,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } } - @Nullable + @NotNull @Override public Element getState() { Element state = new Element("state"); @@ -1318,8 +1399,11 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent @Nullable public FileType getKnownFileTypeOrAssociate(@NotNull VirtualFile file) { FileType type = file.getFileType(); - if (type != UnknownFileType.INSTANCE) return type; - return FileTypeChooser.getKnownFileTypeOrAssociate(file.getName()); + if (type == UnknownFileType.INSTANCE) { + type = FileTypeChooser.associateFileType(file.getName()); + } + + return type; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/WindowWrapperBuilder.java b/platform/platform-impl/src/com/intellij/openapi/ui/WindowWrapperBuilder.java index 852718025147..3f30e572d6a0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/WindowWrapperBuilder.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/WindowWrapperBuilder.java @@ -2,7 +2,6 @@ package com.intellij.openapi.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.WindowWrapper.Mode; -import com.intellij.openapi.util.BooleanGetter; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; @@ -23,8 +22,6 @@ public class WindowWrapperBuilder { @Nullable private JComponent myPreferredFocusedComponent; @Nullable private String myDimensionServiceKey; @Nullable private Runnable myOnShowCallback; - @Nullable private BooleanGetter myOkAction; - @Nullable private BooleanGetter myCancelAction; public WindowWrapperBuilder(@NotNull Mode mode, @NotNull JComponent component) { myMode = mode; @@ -67,18 +64,6 @@ public class WindowWrapperBuilder { return this; } - @NotNull - public WindowWrapperBuilder setOkAction(@Nullable BooleanGetter value) { - myOkAction = value; - return this; - } - - @NotNull - public WindowWrapperBuilder setCancelAction(@Nullable BooleanGetter value) { - myCancelAction = value; - return this; - } - @NotNull public WindowWrapper build() { switch (myMode) { @@ -107,10 +92,7 @@ public class WindowWrapperBuilder { myDialog = builder.myParent != null ? new MyDialogWrapper(builder.myParent, builder.myComponent) : new MyDialogWrapper(builder.myProject, builder.myComponent); - myDialog.setParameters(builder.myDimensionServiceKey, - builder.myPreferredFocusedComponent, - builder.myOkAction, - builder.myCancelAction); + myDialog.setParameters(builder.myDimensionServiceKey, builder.myPreferredFocusedComponent); final Runnable onShowCallback = builder.myOnShowCallback; if (onShowCallback != null) { @@ -182,15 +164,13 @@ public class WindowWrapperBuilder { @Override public void close() { - myDialog.getCancelAction().actionPerformed(null); + myDialog.close(DialogWrapper.CANCEL_EXIT_CODE); } private static class MyDialogWrapper extends DialogWrapper { @NotNull private JComponent myComponent; @Nullable private String myDimensionServiceKey; @Nullable private JComponent myPreferredFocusedComponent; - @Nullable private BooleanGetter myOkAction; - @Nullable private BooleanGetter myCancelAction; public MyDialogWrapper(@Nullable Project project, @NotNull JComponent component) { super(project, true); @@ -203,20 +183,15 @@ public class WindowWrapperBuilder { } public void setParameters(@Nullable String dimensionServiceKey, - @Nullable JComponent preferredFocusedComponent, - @Nullable BooleanGetter okAction, - @Nullable BooleanGetter cancelAction) { + @Nullable JComponent preferredFocusedComponent) { myDimensionServiceKey = dimensionServiceKey; myPreferredFocusedComponent = preferredFocusedComponent; - myOkAction = okAction; - myCancelAction = cancelAction; } @Nullable @Override protected Border createContentPaneBorder() { - if (myOkAction == null && myCancelAction == null) return null; - return super.createContentPaneBorder(); + return null; } @Override @@ -228,25 +203,13 @@ public class WindowWrapperBuilder { @NotNull @Override protected Action[] createActions() { - if (myOkAction == null && myCancelAction == null) return new Action[0]; - return super.createActions(); - } - - @Override - protected void doOKAction() { - if (myOkAction == null || myOkAction.get()) super.doOKAction(); - } - - @Override - public void doCancelAction() { - if (myCancelAction == null || myCancelAction.get()) super.doCancelAction(); + return new Action[0]; } @Nullable @Override protected JComponent createSouthPanel() { - if (myOkAction == null && myCancelAction == null) return null; - return super.createSouthPanel(); + return null; } @Nullable @@ -273,7 +236,6 @@ public class WindowWrapperBuilder { public FrameWindowWrapper(@NotNull WindowWrapperBuilder builder) { assert builder.myMode == Mode.FRAME; - if (builder.myOkAction != null || builder.myCancelAction != null) throw new IllegalArgumentException(); myProject = builder.myProject; myComponent = builder.myComponent; diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java index eb9958e194d1..53b014c785a5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java @@ -973,7 +973,7 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra } myActivated = true; JComponent toFocus = wrapper == null ? null : wrapper.getPreferredFocusedComponent(); - if (toFocus == null) { + if (getRootPane() != null && toFocus == null) { toFocus = getRootPane().getDefaultButton(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/RemoteFileInfoImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/RemoteFileInfoImpl.java index 6738007d6752..4ed1b015e1d9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/RemoteFileInfoImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/RemoteFileInfoImpl.java @@ -331,7 +331,7 @@ public class RemoteFileInfoImpl implements RemoteContentProvider.DownloadingCall remoteFileInfo.removeDownloadingListener(this); } finally { - promise.setError(Promise.createError(errorMessage)); + promise.setError(errorMessage); } } @@ -341,7 +341,7 @@ public class RemoteFileInfoImpl implements RemoteContentProvider.DownloadingCall remoteFileInfo.removeDownloadingListener(this); } finally { - promise.setError(Promise.createError("Cancelled")); + promise.setError("Cancelled"); } } }); diff --git a/platform/platform-impl/src/com/intellij/remote/RemoteProcessHandlerBase.java b/platform/platform-impl/src/com/intellij/remote/RemoteProcessHandlerBase.java index 5a9ad29c8e2a..face26cc3b06 100644 --- a/platform/platform-impl/src/com/intellij/remote/RemoteProcessHandlerBase.java +++ b/platform/platform-impl/src/com/intellij/remote/RemoteProcessHandlerBase.java @@ -1,6 +1,7 @@ package com.intellij.remote; import com.intellij.openapi.util.Pair; +import com.intellij.util.PathMapper; import com.intellij.util.PathMappingSettings; import org.jetbrains.annotations.NotNull; @@ -11,7 +12,7 @@ import java.util.List; */ public interface RemoteProcessHandlerBase { @NotNull - PathMappingSettings getMappingSettings(); + PathMapper getMappingSettings(); Pair obtainRemoteSocket() throws RemoteSdkException; diff --git a/platform/platform-impl/src/org/jetbrains/io/NettyUtil.java b/platform/platform-impl/src/org/jetbrains/io/NettyUtil.java index dea45f7ce246..ba3c9274646f 100644 --- a/platform/platform-impl/src/org/jetbrains/io/NettyUtil.java +++ b/platform/platform-impl/src/org/jetbrains/io/NettyUtil.java @@ -38,9 +38,9 @@ import io.netty.handler.codec.http.cors.CorsHandler; import io.netty.handler.stream.ChunkedWriteHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.concurrency.AsyncPromise; -import org.jetbrains.concurrency.Promise; import org.jetbrains.ide.PooledThreadExecutor; +import org.jetbrains.util.concurrency.AsyncPromise; +import org.jetbrains.util.concurrency.Promise; import java.io.IOException; import java.net.BindException; @@ -143,8 +143,12 @@ public final class NettyUtil { @SuppressWarnings("ThrowableResultOfMethodCallIgnored") Throwable cause = future.cause(); if (promise != null) { - //noinspection ThrowableResultOfMethodCallIgnored - promise.setError(cause == null ? Promise.createError("Cannot connect: unknown error") : cause); + if (cause == null) { + promise.setError("Cannot connect: unknown error"); + } + else { + promise.setError(cause); + } } return null; } diff --git a/platform/platform-impl/src/org/jetbrains/util/concurrency/AsyncPromise.kt b/platform/platform-impl/src/org/jetbrains/util/concurrency/AsyncPromise.kt new file mode 100644 index 000000000000..2bce43424468 --- /dev/null +++ b/platform/platform-impl/src/org/jetbrains/util/concurrency/AsyncPromise.kt @@ -0,0 +1,265 @@ +/* + * 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.util.concurrency + +import com.intellij.openapi.diagnostic.Logger +import org.jetbrains.concurrency.Obsolescent +import java.util.ArrayList + +public class AsyncPromise : Promise { + companion object { + private val LOG = Logger.getInstance(javaClass>()) + + public val OBSOLETE_ERROR: RuntimeException = MessageError("Obsolete") + + private fun setHandler(oldConsumer: ((T) -> Unit)?, newConsumer: (T) -> Unit): (T) -> Unit { + return when (oldConsumer) { + null -> newConsumer + is CompoundConsumer<*> -> { + (oldConsumer as CompoundConsumer).add(newConsumer) + return oldConsumer + } + else -> CompoundConsumer(oldConsumer, newConsumer) + } + } + + private class CompoundConsumer(c1: (T) -> Unit, c2: (T) -> Unit) : (T) -> Unit { + private var consumers: MutableList<(T) -> Unit>? = ArrayList() + + init { + synchronized (this) { + consumers!!.add(c1) + consumers!!.add(c2) + } + } + + override fun invoke(p1: T) { + var list = synchronized (this) { + var list = consumers!! + consumers = null + list + } + + for (consumer in list) { + if (!consumer.isObsolete()) { + consumer(p1) + } + } + } + + fun add(consumer: (T) -> Unit) { + synchronized (this) { + consumers?.add(consumer) + } + } + } + } + + private volatile var done: ((T) -> Unit)? = null + private volatile var rejected: ((Throwable) -> Unit)? = null + + override volatile var state = Promise.State.PENDING + private set + + // result object or error message + private volatile var result: Any? = null + + override fun done(done: (T) -> Unit): Promise { + if (done.isObsolete()) { + return this + } + + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> { + @suppress("UNCHECKED_CAST") + done(result as T) + return this + } + Promise.State.REJECTED -> return this + } + + this.done = setHandler(this.done, done) + return this + } + + override fun rejected(rejected: (Throwable) -> Unit): Promise { + if (rejected.isObsolete()) { + return this + } + + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> return this + Promise.State.REJECTED -> { + rejected(result as Throwable) + return this + } + } + + this.rejected = setHandler(this.rejected, rejected) + return this + } + + public fun get(): T { + @suppress("UNCHECKED_CAST") + return when (state) { + Promise.State.FULFILLED -> result as T + else -> null + } + } + + override fun then(done: (T) -> SUB_RESULT): Promise { + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> { + @suppress("UNCHECKED_CAST") + return DonePromise(done(result as T)) + } + Promise.State.REJECTED -> return RejectedPromise(result as Throwable) + } + + val promise = AsyncPromise() + addHandlers({ + promise.catchError { + if (done is Obsolescent && done.isObsolete()) { + promise.setError(OBSOLETE_ERROR) + } + else { + val subResult = done(it) + if (subResult is Promise<*>) { + subResult.notify(promise) + } + else { + promise.setResult(subResult) + } + } + } + }, + { promise.setError(it) } + ) + return promise + } + + override fun notify(child: AsyncPromise) { + if (child == this) { + throw IllegalStateException("Child must no be equals to this") + } + + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> { + @suppress("UNCHECKED_CAST") + child.setResult(result as T) + return + } + Promise.State.REJECTED -> { + child.setError(result as Throwable) + return + } + } + + addHandlers({ child.catchError { child.setResult(it) } }, { child.setError(it) }) + } + + override fun processed(fulfilled: AsyncPromise): Promise { + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> { + @suppress("UNCHECKED_CAST") + fulfilled.setResult(result as T) + return this + } + Promise.State.REJECTED -> { + fulfilled.setError(result as Throwable) + return this + } + } + + addHandlers({ fulfilled.catchError { fulfilled.setResult(it) } }, { fulfilled.setError(it) }) + return this + } + + private fun addHandlers(done: (T) -> Unit, rejected: (Throwable) -> Unit) { + this.done = setHandler(this.done, done) + this.rejected = setHandler(this.rejected, rejected) + } + + public fun setResult(result: T) { + if (state != Promise.State.PENDING) { + return + } + + state = Promise.State.FULFILLED + this.result = result + + val done = this.done + clearHandlers() + if (done != null && !done.isObsolete()) { + done(result) + } + } + + public fun setError(error: String): Boolean = setError(MessageError(error)) + + public fun setError(error: Throwable): Boolean { + if (state != Promise.State.PENDING) { + return false + } + + state = Promise.State.REJECTED + result = error + + val rejected = this.rejected + clearHandlers() + if (rejected != null) { + if (!rejected.isObsolete()) { + rejected(error) + } + } + else { + Promise.logError(LOG, error) + } + return true + } + + private fun clearHandlers() { + done = null + rejected = null + } + + override fun processed(processed: (T) -> Unit): Promise { + done(processed) + rejected { processed(null) } + return this + } +} + +fun ((T) -> Unit).isObsolete() = this is Obsolescent && this.isObsolete() + +public inline fun AsyncPromise.catchError(task: () -> Unit) { + try { + task() + } + catch (e: Throwable) { + setError(e) + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/org/jetbrains/util/concurrency/DonePromise.kt b/platform/platform-impl/src/org/jetbrains/util/concurrency/DonePromise.kt new file mode 100644 index 000000000000..e91371086f14 --- /dev/null +++ b/platform/platform-impl/src/org/jetbrains/util/concurrency/DonePromise.kt @@ -0,0 +1,55 @@ +/* + * 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.util.concurrency + +import org.jetbrains.concurrency.Obsolescent + +private class DonePromise(private val result: T) : Promise { + override val state: Promise.State + get() = Promise.State.FULFILLED + + override fun done(done: (T) -> Unit): Promise { + if (!done.isObsolete()) { + done(result) + } + return this + } + + override fun processed(fulfilled: AsyncPromise): Promise { + fulfilled.setResult(result) + return this + } + + override fun processed(processed: (T) -> Unit): Promise { + done(processed) + return this + } + + override fun rejected(rejected: (Throwable) -> Unit) = this + + override fun then(done: (T) -> SUB_RESULT): Promise { + return if (done is Obsolescent && (done).isObsolete()) { + RejectedPromise("obsolete") + } + else { + ResolvedPromise(done(result)) + } + } + + override fun notify(child: AsyncPromise) { + child.setResult(result) + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/org/jetbrains/util/concurrency/Promise.kt b/platform/platform-impl/src/org/jetbrains/util/concurrency/Promise.kt new file mode 100644 index 000000000000..f6df26150bcf --- /dev/null +++ b/platform/platform-impl/src/org/jetbrains/util/concurrency/Promise.kt @@ -0,0 +1,124 @@ +/* + * 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.util.concurrency + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.util.Consumer +import org.jetbrains.concurrency + +public interface Promise { + public enum class State { + PENDING, + FULFILLED, + REJECTED + } + + public val state: State + + public fun done(done: (T) -> Unit): Promise + + public fun processed(fulfilled: AsyncPromise): Promise + + public fun rejected(rejected: (Throwable) -> Unit): Promise + + public fun processed(processed: (T) -> Unit): Promise + + public fun then(done: (T) -> SUB_RESULT): Promise + + fun notify(child: AsyncPromise) + + companion object { + /** + * Log error if not message error + */ + public fun logError(logger: Logger, e: Throwable) { + if (e !is MessageError || ApplicationManager.getApplication().isUnitTestMode()) { + logger.error(e) + } + } + + public fun all(promises: Collection>): Promise<*> = all(promises, null) + + public fun all(promises: Collection>, totalResult: T): Promise { + if (promises.isEmpty()) { + @suppress("UNCHECKED_CAST") + return DONE as Promise + } + + val totalPromise = AsyncPromise() + val done = CountDownConsumer(promises.size(), totalPromise, totalResult) + val rejected = {it: Throwable -> Unit + if (totalPromise.state == Promise.State.PENDING) { + totalPromise.setError(it) + } + } + + for (promise in promises) { + //noinspection unchecked + promise.done(done) + promise.rejected(rejected) + } + return totalPromise + } + } +} + +private val DONE: Promise<*> = DonePromise(null) +private val REJECTED: Promise<*> = RejectedPromise(MessageError("rejected")) + +private class MessageError(error: String) : RuntimeException(error) { + public synchronized fun fillInStackTrace(): Throwable? = this +} + +public fun RejectedPromise(error: String): Promise = RejectedPromise(MessageError(error)) + +public fun ResolvedPromise(): Promise<*> = DONE + +public fun ResolvedPromise(result: T): Promise = DonePromise(result) + +public fun concurrency.Promise.toPromise(): AsyncPromise { + val promise = AsyncPromise() + done(object : Consumer { + override fun consume(value: T) { + promise.setResult(value) + } + }) + .rejected(object : Consumer { + override fun consume(throwable: Throwable) { + promise.setError(throwable) + } + }) + return promise +} + +public fun Promise.toPromise(): concurrency.AsyncPromise { + val promise = concurrency.AsyncPromise() + done { promise.setResult(it) } + .rejected { promise.setError(it) } + return promise +} + +private class CountDownConsumer(private volatile var countDown: Int, private val promise: AsyncPromise, private val totalResult: T) : (T) -> Unit { + override fun invoke(p1: T) { + if (--countDown == 0) { + promise.setResult(totalResult) + } + } +} + +public val Promise<*>.pending: Boolean + get() = state == Promise.State.PENDING \ No newline at end of file diff --git a/platform/platform-impl/src/org/jetbrains/util/concurrency/RejectedPromise.kt b/platform/platform-impl/src/org/jetbrains/util/concurrency/RejectedPromise.kt new file mode 100644 index 000000000000..0e86c098a171 --- /dev/null +++ b/platform/platform-impl/src/org/jetbrains/util/concurrency/RejectedPromise.kt @@ -0,0 +1,49 @@ +/* + * 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.util.concurrency + +class RejectedPromise(private val error: Throwable) : Promise { + override public val state: Promise.State + get() = Promise.State.REJECTED + + override fun done(done: (T) -> Unit) = this + + override fun processed(fulfilled: AsyncPromise): Promise { + fulfilled.setError(error) + return this + } + + override fun rejected(rejected: (Throwable) -> Unit): Promise { + if (!rejected.isObsolete()) { + rejected(error) + } + return this + } + + override fun processed(processed: (T) -> Unit): RejectedPromise { + processed(null) + return this + } + + override fun then(done: (T) -> SUB_RESULT): Promise { + @suppress("UNCHECKED_CAST") + return this as Promise + } + + override fun notify(child: AsyncPromise) { + child.setError(error) + } +} \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index d0723d9d1cfb..1c82db3534a7 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -473,6 +473,8 @@ action.GotoTypeDeclaration.text=_Type Declaration action.GotoTypeDeclaration.description=Navigate to the declaration of the symbol's type at caret action.GotoSuperMethod.text=S_uper Method action.GotoSuperMethod.description=Navigate to the declaration of the method that current method overrides or implements +action.GotoSuperClass.text=S_uper Class +action.GotoSuperClass.description=Navigate to the declaration of the class that current class extends or implements action.GotoTest.text=T_est action.GotoTest.description=Navigate to the test of the selected class action.GotoTestSubject.text=T_est Subject diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index dbb2f09d4ee4..0d36f077cb13 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -384,7 +384,7 @@ label.collapse.by.default=Collapse by default: checkbox.show.code.folding.outline=Show code folding outline group.tab.appearance=Tab Appearance editbox.tab.limit=Tab limit: -checkbox.smart.tab.reuse=Navigation from not-modified tab will reuse it +checkbox.smart.tab.reuse=Navigation from non-modified tab will reuse it combobox.editor.tab.placement=Placement: checkbox.editor.tabs.in.single.row=Show tabs in single row checkbox.editor.tabs.show.close.button=Show "close" button on editor tabs @@ -590,9 +590,13 @@ auto.import=Auto Import checkbox.collapse.suppress.warnings=@SuppressWarnings checkbox.collapse.end.of.line.comments=End of line comments sequence title.other.languages=Other Languages +title.tabs.and.indents=Tabs and Indents + launcher.script.title=Create Launcher Script launcher.script.overwrite=The file ''{0}'' already exists. Would you like to overwrite it? launcher.script.sudo.prompt=Please enter your password to create a launcher script in a ''{0}'' + +desktop.entry.title=Create Desktop Entry desktop.entry.success=You may now exit {0} and start it from the system menu
    \ (restart a session if a new entry seem not to appear). desktop.entry.xdg.missing=Unfortunately, xdg-utils seem to be unavailable on your system. @@ -602,7 +606,6 @@ desktop.entry.checking=Checking... desktop.entry.preparing=Preparing entry... desktop.entry.installing=Installing entry... desktop.entry.sudo.prompt=Please enter your password to create a desktop entry -title.tabs.and.indents=Tabs and Indents watcher.slow.sync=External file changes sync may be slow watcher.exe.not.found=Native file watcher executable not found diff --git a/platform/platform-resources-en/src/messages/ExecutionBundle.properties b/platform/platform-resources-en/src/messages/ExecutionBundle.properties index 5d56fd033d71..cad5070ce93e 100644 --- a/platform/platform-resources-en/src/messages/ExecutionBundle.properties +++ b/platform/platform-resources-en/src/messages/ExecutionBundle.properties @@ -362,3 +362,13 @@ failed.to.create.output.file=Failed to create output file ''{0}'' script.execution.timeout=Script execution took more than {0} seconds. junit.configuration.configure.junit.test.kind.label=&Test kind: + +sm.test.runner.magnitude.completed.failed.title=Completed +sm.test.runner.magnitude.passed.title=Passed +sm.test.runner.magnitude.skipped.failed.title=Skipped +sm.test.runner.magnitude.not.run.failed.title=Not run +sm.test.runner.magnitude.running.failed.title=Running... +sm.test.runner.magnitude.terminated.failed.title=Terminated +sm.test.runner.magnitude.ignored.failed.title=Ignored +sm.test.runner.magnitude.assertion.failed.title=Assertion failed +sm.test.runner.magnitude.testerror.title=Error diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties index 65eec3e657b2..b337763d4a03 100644 --- a/platform/platform-resources-en/src/messages/IdeBundle.properties +++ b/platform/platform-resources-en/src/messages/IdeBundle.properties @@ -1182,11 +1182,3 @@ new.dir.project.error.buck=Project directory name must not contain the ''$'' cha go.to.file.toolwindow.title=Files matching pattern go.to.class.toolwindow.title=Classes matching pattern go.to.class.dumb.mode.message=Go To Class action is not available until indices are built, using Go To File instead - -checkbox.color.blindness=Adjust for red-green color deficiency -# define at least one name to show checkbox -# define more that one name to show combobox -combobox.color.blindness.protanopia=Protanopia -combobox.color.blindness.deuteranopia= -combobox.color.blindness.tritanopia= -combobox.color.blindness.achromatopsia= diff --git a/platform/platform-resources-en/src/messages/SMTestsRunnerBundle.properties b/platform/platform-resources-en/src/messages/SMTestsRunnerBundle.properties index 43a78f4263a2..d5c4bd3b0a28 100644 --- a/platform/platform-resources-en/src/messages/SMTestsRunnerBundle.properties +++ b/platform/platform-resources-en/src/messages/SMTestsRunnerBundle.properties @@ -35,15 +35,5 @@ sm.test.runner.ui.tabs.statistics.columns.results.count.msg.passed=P:{0} sm.test.runner.ui.tabs.statistics.columns.results.count.msg.ignored=I:{0} sm.test.runner.ui.tabs.statistics.columns.results.no.tests= -sm.test.runner.magnitude.completed.failed.title=Completed -sm.test.runner.magnitude.passed.title=Passed -sm.test.runner.magnitude.skipped.failed.title=Skipped -sm.test.runner.magnitude.not.run.failed.title=Not run -sm.test.runner.magnitude.running.failed.title=Running... -sm.test.runner.magnitude.terminated.failed.title=Terminated -sm.test.runner.magnitude.ignored.failed.title=Ignored -sm.test.runner.magnitude.assertion.failed.title=Assertion failed -sm.test.runner.magnitude.testerror.title=Error - sm.test.runner.states.suite.is.empty=Empty test suite. sm.test.runner.states.test.is.ignored=Test ignored. \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/UIBundle.properties b/platform/platform-resources-en/src/messages/UIBundle.properties index b3513eafe5c7..e9dd4ba702ca 100644 --- a/platform/platform-resources-en/src/messages/UIBundle.properties +++ b/platform/platform-resources-en/src/messages/UIBundle.properties @@ -165,3 +165,11 @@ tool.window.quick.access.title=Tool Windows Quick Access tool.window.quick.access.message=Hover over the icon below to access tool windows\nClick the icon to make tool windows buttons visible got.it=Got it! + +color.blindness.checkbox.text=Adjust for color deficiency: +# define at least one name to show checkbox +# define more that one name to show combobox +color.blindness.protanopia.name=Protanopia (red) +color.blindness.deuteranopia.name=Deuteranopia (green) +color.blindness.tritanopia.name= +color.blindness.achromatopsia.name= diff --git a/platform/platform-resources-en/src/messages/XDebuggerBundle.properties b/platform/platform-resources-en/src/messages/XDebuggerBundle.properties index 7c9c2284b7c7..a38e9a4eb5e8 100644 --- a/platform/platform-resources-en/src/messages/XDebuggerBundle.properties +++ b/platform/platform-resources-en/src/messages/XDebuggerBundle.properties @@ -33,14 +33,15 @@ xbreakpoints.conditions.group.title=Conditions suspend.policy.panel.title=Suspend policy suspend.policy.panel.suspend=&Suspend -suspend.policy.panel.all=All -suspend.policy.panel.thread=Thread +suspend.policy.panel.all=&All +suspend.policy.panel.thread=&Thread suspend.policy.panel.makeDefault=Make De&fault xbreakpoints.properties.actions.group.title=Actions xbreakpoints.log.message.checkbox=Log &message to console xbreakpoints.log.expression.checkbox=Log evaluated &expression: -xbreakpoints.condition.checkbox=&Condition +xbreakpoints.condition.checkbox=&Condition: +xbreakpoints.enabled.label=Enable&d xbreakpoint.group.depends.on=Depends on xbreakpoint.label.after.breakpoint.was.hit=After breakpoint was hit xbreakpoint.radio.disable.again=Disable again diff --git a/platform/platform-resources/src/DefaultColorSchemesManager.xml b/platform/platform-resources/src/DefaultColorSchemesManager.xml index 87b8e5184523..c22759c23e7d 100644 --- a/platform/platform-resources/src/DefaultColorSchemesManager.xml +++ b/platform/platform-resources/src/DefaultColorSchemesManager.xml @@ -644,7 +644,7 @@
    \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebugProcessImpl.kt b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebugProcessImpl.kt index c94d597b600c..53cda347eee3 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebugProcessImpl.kt +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebugProcessImpl.kt @@ -31,8 +31,6 @@ import com.intellij.xdebugger.breakpoints.XLineBreakpoint import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider import com.intellij.xdebugger.frame.XSuspendContext import com.intellij.xdebugger.stepping.XSmartStepIntoHandler -import org.jetbrains.concurrency.AsyncFunction -import org.jetbrains.concurrency.Promise import org.jetbrains.debugger.connection.VmConnection import org.jetbrains.debugger.frame.SuspendContextImpl import java.util.concurrent.ConcurrentMap @@ -52,30 +50,37 @@ public abstract class DebugProcessImpl>(session: XDebugSessi protected val urlToFileCache: ConcurrentMap = ContainerUtil.newConcurrentMap() - private var processBreakpointConditionsAtIdeSide = false + public var processBreakpointConditionsAtIdeSide: Boolean = false private val _breakpointHandlers: Array> by Delegates.lazy { createBreakpointHandlers() } init { connection.addListener(object : SocketConnectionListener { override fun statusChanged(status: ConnectionStatus) { - if (status === ConnectionStatus.DISCONNECTED || status === ConnectionStatus.DETACHED) { - if (status === ConnectionStatus.DETACHED) { - if (getRealProcessHandler() != null) { - // here must we must use effective process handler - getProcessHandler().detachProcess() + when (status) { + ConnectionStatus.DISCONNECTED, ConnectionStatus.DETACHED -> { + if (status == ConnectionStatus.DETACHED) { + if (realProcessHandler != null) { + // here must we must use effective process handler + getProcessHandler().detachProcess() + } } + getSession().stop() + } + ConnectionStatus.CONNECTION_FAILED -> { + getSession().reportError(status.getStatusText()) + getSession().stop() + } + else -> { + getSession().rebuildViews() } - getSession().stop() - } - else { - getSession().rebuildViews() } } }) } - protected final fun getRealProcessHandler(): ProcessHandler? = executionResult?.getProcessHandler() + protected final val realProcessHandler: ProcessHandler? + get() = executionResult?.getProcessHandler() override final fun getSmartStepIntoHandler() = smartStepIntoHandler @@ -83,19 +88,16 @@ public abstract class DebugProcessImpl>(session: XDebugSessi override final fun getEditorsProvider() = editorsProvider - public fun setProcessBreakpointConditionsAtIdeSide(value: Boolean) { - processBreakpointConditionsAtIdeSide = value - } - - public fun getVm(): Vm? = connection.getVm() + public val vm: Vm? + get() = connection.vm protected abstract fun createBreakpointHandlers(): Array> private fun updateLastCallFrame() { - lastCallFrame = getVm()?.getSuspendContextManager()?.getContext()?.getTopFrame() + lastCallFrame = vm?.getSuspendContextManager()?.getContext()?.getTopFrame() } - override final fun checkCanPerformCommands() = getVm() != null + override final fun checkCanPerformCommands() = vm != null override final fun isValuesCustomSorted() = true @@ -132,7 +134,7 @@ public abstract class DebugProcessImpl>(session: XDebugSessi } protected final fun continueVm(stepAction: StepAction) { - val suspendContextManager = getVm()!!.getSuspendContextManager() + val suspendContextManager = vm!!.getSuspendContextManager() if (stepAction === StepAction.CONTINUE) { if (suspendContextManager.getContext() == null) { // on resumed we ask session to resume, and session then call our "resume", but we have already resumed, so, we don't need to send "continue" message @@ -151,7 +153,7 @@ public abstract class DebugProcessImpl>(session: XDebugSessi } protected final fun setOverlay() { - getVm()!!.getSuspendContextManager().setOverlayMessage("Paused in debugger") + vm!!.getSuspendContextManager().setOverlayMessage("Paused in debugger") } protected final fun processBreakpoint(suspendContext: SuspendContext, breakpoint: XBreakpoint<*>, xSuspendContext: SuspendContextImpl) { @@ -209,7 +211,7 @@ public abstract class DebugProcessImpl>(session: XDebugSessi } override final fun startPausing() { - connection.getVm().getSuspendContextManager().suspend().rejected(RejectErrorReporter(getSession(), "Cannot pause")) + connection.vm.getSuspendContextManager().suspend().rejected(RejectErrorReporter(getSession(), "Cannot pause")) } override final fun getCurrentStateMessage() = connection.getState().getMessage() @@ -223,8 +225,4 @@ public abstract class DebugProcessImpl>(session: XDebugSessi } public abstract fun getLocationsForBreakpoint(breakpoint: XLineBreakpoint<*>, onlySourceMappedBreakpoints: Boolean): List -} - -public inline fun asyncPromise(inlineOptions(InlineOption.ONLY_LOCAL_RETURN) task: () -> Promise): AsyncFunction = object : AsyncFunction { - override fun `fun`(aVoid: Void?) = task() } \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebuggableRunConfiguration.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebuggableRunConfiguration.java new file mode 100644 index 000000000000..3c8c92fdd8a6 --- /dev/null +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebuggableRunConfiguration.java @@ -0,0 +1,43 @@ +/* + * 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.debugger; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.ExecutionResult; +import com.intellij.execution.configurations.RunConfiguration; +import com.intellij.execution.configurations.RunProfile; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebugSession; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.InetSocketAddress; + +public interface DebuggableRunConfiguration extends RunConfiguration { + @NotNull + InetSocketAddress computeDebugAddress() throws ExecutionException; + + @NotNull + XDebugProcess createDebugProcess(@NotNull InetSocketAddress socketAddress, + @NotNull XDebugSession session, + @Nullable ExecutionResult executionResult, + @NotNull ExecutionEnvironment environment) throws ExecutionException; + + interface RunConfigurationWithConditionalRun { + boolean canRun(@NotNull String executorId, @NotNull RunProfile profile); + } +} \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/LineBreakpointManager.kt b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/LineBreakpointManager.kt index 4bb5d2a70b91..f4d50b03248b 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/LineBreakpointManager.kt +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/LineBreakpointManager.kt @@ -16,14 +16,15 @@ package org.jetbrains.debugger import com.intellij.icons.AllIcons -import com.intellij.util.Consumer import com.intellij.util.SmartList import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.MultiMap import com.intellij.xdebugger.XSourcePosition import com.intellij.xdebugger.breakpoints.XLineBreakpoint import gnu.trove.THashSet -import org.jetbrains.concurrency.Promise +import org.jetbrains.util.concurrency +import org.jetbrains.util.concurrency.Promise +import org.jetbrains.util.concurrency.ResolvedPromise public abstract class LineBreakpointManager(private val vm: Vm, private val debugProcess: DebugProcessImpl<*>) { private val ideToVmBreakpoint = MultiMap.createSmart, Breakpoint>() @@ -69,18 +70,15 @@ public abstract class LineBreakpointManager(private val vm: Vm, private val debu else { val breakpointManager = vm.getBreakpointManager() for (vmBreakpoint in target) { - if (!vmBreakpoint.isEnabled()) { - breakpointManager.flush(vmBreakpoint.enabled(true)).rejected(object : Consumer { - override fun consume(error: Throwable) { - debugProcess.getSession().updateBreakpointPresentation(breakpoint, AllIcons.Debugger.Db_invalid_breakpoint, error.getMessage()) - } - }) + if (!vmBreakpoint.enabled) { + vmBreakpoint.enabled = true + breakpointManager.flush(vmBreakpoint).rejected { debugProcess.getSession().updateBreakpointPresentation(breakpoint, AllIcons.Debugger.Db_invalid_breakpoint, it.getMessage()) } } } } } - public fun removeBreakpoint(breakpoint: XLineBreakpoint<*>, temporary: Boolean): Promise { + public fun removeBreakpoint(breakpoint: XLineBreakpoint<*>, temporary: Boolean): concurrency.Promise<*> { val disable = temporary && vm.getBreakpointManager().getMuteMode() !== BreakpointManager.MUTE_MODE.NONE beforeBreakpointRemoved(breakpoint, disable) return doRemoveBreakpoint(breakpoint, disable) @@ -89,7 +87,7 @@ public abstract class LineBreakpointManager(private val vm: Vm, private val debu protected open fun beforeBreakpointRemoved(breakpoint: XLineBreakpoint<*>, disable: Boolean) { } - public fun doRemoveBreakpoint(breakpoint: XLineBreakpoint<*>, disable: Boolean): Promise { + public fun doRemoveBreakpoint(breakpoint: XLineBreakpoint<*>, disable: Boolean): concurrency.Promise<*> { var vmBreakpoints: Collection = emptySet() synchronized (lock) { if (disable) { @@ -112,7 +110,7 @@ public abstract class LineBreakpointManager(private val vm: Vm, private val debu vmToIdeBreakpoint.remove(vmBreakpoint, breakpoint) if (vmToIdeBreakpoint.containsKey(vmBreakpoint)) { // we must not remove vm breakpoint - it is used for another ide breakpoints - return Promise.DONE + return ResolvedPromise() } } } @@ -120,14 +118,15 @@ public abstract class LineBreakpointManager(private val vm: Vm, private val debu } if (ContainerUtil.isEmpty(vmBreakpoints)) { - return Promise.DONE + return ResolvedPromise() } val breakpointManager = vm.getBreakpointManager() - val promises = SmartList>() + val promises = SmartList>() if (disable) { for (vmBreakpoint in vmBreakpoints) { - promises.add(breakpointManager.flush(vmBreakpoint.enabled(false))) + vmBreakpoint.enabled = false + promises.add(breakpointManager.flush(vmBreakpoint)) } } else { @@ -135,7 +134,7 @@ public abstract class LineBreakpointManager(private val vm: Vm, private val debu promises.add(breakpointManager.remove(vmBreakpoint)) } } - return Promise.all(promises) + return concurrency.Promise.all(promises) } public fun setBreakpoint(breakpoint: XLineBreakpoint<*>, locations: List) { @@ -192,7 +191,7 @@ public abstract class LineBreakpointManager(private val vm: Vm, private val debu } } - public fun removeAllBreakpoints(): Promise { + public fun removeAllBreakpoints(): Promise<*> { synchronized (lock) { ideToVmBreakpoint.clear() vmToIdeBreakpoint.clear() diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RemoteDebugConfiguration.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RemoteDebugConfiguration.java new file mode 100644 index 000000000000..d4102e79f7f2 --- /dev/null +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RemoteDebugConfiguration.java @@ -0,0 +1,166 @@ +/* + * 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.debugger; + +import com.intellij.execution.Executor; +import com.intellij.execution.configuration.EmptyRunProfileState; +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.configurations.LocatableConfigurationBase; +import com.intellij.execution.configurations.RunConfiguration; +import com.intellij.execution.configurations.RunProfileState; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.runners.RunConfigurationWithSuppressedDefaultRunAction; +import com.intellij.openapi.options.SettingsEditor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.GuiUtils; +import com.intellij.ui.PortField; +import com.intellij.util.ThreeState; +import com.intellij.util.net.NetUtils; +import com.intellij.util.ui.FormBuilder; +import com.intellij.util.xmlb.SerializationFilter; +import com.intellij.util.xmlb.SkipEmptySerializationFilter; +import com.intellij.util.xmlb.XmlSerializer; +import com.intellij.util.xmlb.annotations.Attribute; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.net.InetSocketAddress; + +public abstract class RemoteDebugConfiguration extends LocatableConfigurationBase implements RunConfigurationWithSuppressedDefaultRunAction, DebuggableRunConfiguration { + private final SerializationFilter serializationFilter = new SkipEmptySerializationFilter() { + @Override + protected ThreeState accepts(@NotNull String name, @NotNull Object beanValue) { + return name.equals("port") ? ThreeState.fromBoolean(!beanValue.equals(defaultPort)) : ThreeState.UNSURE; + } + }; + + private String host; + + private int port; + private final int defaultPort; + + public RemoteDebugConfiguration(Project project, @NotNull ConfigurationFactory factory, String name, int defaultPort) { + super(project, factory, name); + + port = defaultPort; + this.defaultPort = defaultPort; + } + + @Nullable + @Attribute + public String getHost() { + return host; + } + + public void setHost(@Nullable String value) { + if (StringUtil.isEmpty(value) || value.equals("localhost") || value.equals("127.0.0.1")) { + host = null; + } + else { + host = value; + } + } + + @Attribute + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + @NotNull + @Override + public SettingsEditor getConfigurationEditor() { + return new RemoteDebugConfigurationSettingsEditor(); + } + + @Nullable + @Override + public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment env) { + return EmptyRunProfileState.INSTANCE; + } + + @Override + public RunConfiguration clone() { + RemoteDebugConfiguration configuration = (RemoteDebugConfiguration)super.clone(); + configuration.host = host; + configuration.port = port; + return configuration; + } + + @Override + public void readExternal(Element element) throws InvalidDataException { + super.readExternal(element); + + XmlSerializer.deserializeInto(this, element); + if (port <= 0) { + port = defaultPort; + } + } + + @Override + public void writeExternal(Element element) throws WriteExternalException { + super.writeExternal(element); + + XmlSerializer.serializeInto(this, element, serializationFilter); + } + + @NotNull + @Override + public InetSocketAddress computeDebugAddress() { + if (host == null) { + return new InetSocketAddress(NetUtils.getLoopbackAddress(), port); + } + else { + return new InetSocketAddress(host, getPort()); + } + } + + private final class RemoteDebugConfigurationSettingsEditor extends SettingsEditor { + private final JTextField hostField; + private final PortField portField; + + public RemoteDebugConfigurationSettingsEditor() { + hostField = GuiUtils.createUndoableTextField(); + portField = new PortField(defaultPort, 1024); + } + + @Override + protected void resetEditorFrom(RemoteDebugConfiguration configuration) { + hostField.setText(StringUtil.notNullize(configuration.host, "localhost")); + portField.setNumber(configuration.port); + } + + @Override + protected void applyEditorTo(RemoteDebugConfiguration configuration) { + configuration.setHost(hostField.getText()); + configuration.setPort(portField.getNumber()); + } + + @NotNull + @Override + protected JComponent createEditor() { + return FormBuilder.createFormBuilder().addLabeledComponent("&Host:", hostField).addLabeledComponent("&Port:", portField).getPanel(); + } + } +} \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/RemoteVmConnection.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/RemoteVmConnection.java deleted file mode 100644 index 7caa7ad3f0b6..000000000000 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/RemoteVmConnection.java +++ /dev/null @@ -1,184 +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 org.jetbrains.debugger.connection; - -import com.intellij.ide.browsers.WebBrowser; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.Condition; -import com.intellij.ui.ColoredListCellRenderer; -import com.intellij.ui.components.JBList; -import com.intellij.util.Consumer; -import com.intellij.util.Function; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.io.socketConnection.ConnectionStatus; -import io.netty.bootstrap.Bootstrap; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.concurrency.AsyncPromise; -import org.jetbrains.concurrency.Promise; -import org.jetbrains.debugger.Vm; -import org.jetbrains.io.NettyUtil; -import org.jetbrains.rpc.CommandProcessor; - -import javax.swing.*; -import java.net.InetSocketAddress; -import java.util.Collection; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicReference; - -public abstract class RemoteVmConnection extends VmConnection { - private final AtomicReference connectCancelHandler = new AtomicReference(); - - @Nullable - @Override - public WebBrowser getBrowser() { - return null; - } - - @NotNull - public abstract Bootstrap createBootstrap(@NotNull InetSocketAddress address, @NotNull AsyncPromise promise); - - public void open(@NotNull InetSocketAddress address) { - open(address, null); - } - - public void open(@NotNull final InetSocketAddress address, final Condition stopCondition) { - setState(ConnectionStatus.WAITING_FOR_CONNECTION, "Connecting to " + address.getHostName() + ":" + address.getPort()); - final Future future = ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - @Override - public void run() { - if (Thread.interrupted()) { - return; - } - - final AsyncPromise result = new AsyncPromise(); - connectCancelHandler.set(new Runnable() { - @Override - public void run() { - result.setError(Promise.createError("Closed explicitly")); - } - }); - - AsyncPromise connectionPromise = new AsyncPromise(); - NettyUtil.connect(createBootstrap(address, result), address, connectionPromise, stopCondition == null ? NettyUtil.DEFAULT_CONNECT_ATTEMPT_COUNT : -1, stopCondition); - connectionPromise.rejected(new Consumer() { - @Override - public void consume(Throwable error) { - result.setError(error); - } - }); - - result - .done(new Consumer() { - @Override - public void consume(@NotNull Vm vm) { - RemoteVmConnection.this.vm = vm; - setState(ConnectionStatus.CONNECTED, "Connected to " + connectedAddressToPresentation(address, vm)); - startProcessing(); - } - }) - .rejected(new Consumer() { - @Override - public void consume(Throwable error) { - Promise.logError(CommandProcessor.LOG, error); - setState(ConnectionStatus.CONNECTION_FAILED, error.getMessage()); - } - }) - .processed(new Consumer() { - @Override - public void consume(Vm vm) { - connectCancelHandler.set(null); - } - }); - } - }); - connectCancelHandler.set(new Runnable() { - @Override - public void run() { - future.cancel(true); - } - }); - } - - @NotNull - protected String connectedAddressToPresentation(@NotNull InetSocketAddress address, @NotNull Vm vm) { - return address.getHostName() + ":" + address.getPort(); - } - - @NotNull - @Override - public Promise detachAndClose() { - Promise callback; - try { - Runnable runnable = connectCancelHandler.getAndSet(null); - if (runnable != null) { - runnable.run(); - } - } - finally { - callback = super.detachAndClose(); - } - return callback; - } - - @NotNull - public static Promise chooseDebuggee(@NotNull final Collection targets, final int selectedIndex, @NotNull final Function itemToString) { - if (targets.size() == 1) { - return Promise.resolve(ContainerUtil.getFirstItem(targets)); - } - else if (targets.isEmpty()) { - return Promise.reject("No tabs to inspect"); - } - - final AsyncPromise result = new AsyncPromise(); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - final JBList list = new JBList(targets); - list.setCellRenderer(new ColoredListCellRenderer() { - @Override - protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { - //noinspection unchecked - append(itemToString.fun((T)value)); - } - }); - if (selectedIndex != -1) { - list.setSelectedIndex(selectedIndex); - } - - JBPopupFactory.getInstance(). - createListPopupBuilder(list). - setTitle("Choose Page to Debug"). - setItemChoosenCallback(new Runnable() { - @Override - public void run() { - @SuppressWarnings("unchecked") - T value = (T)list.getSelectedValue(); - if (value == null) { - result.setError(Promise.createError("No target to inspect")); - } - else { - result.setResult(value); - } - } - }). - createPopup().showInFocusCenter(); - } - }); - return result; - } -} \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/RemoteVmConnection.kt b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/RemoteVmConnection.kt new file mode 100644 index 000000000000..c3dca8af9ec3 --- /dev/null +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/RemoteVmConnection.kt @@ -0,0 +1,129 @@ +/* + * 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.debugger.connection + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.ui.popup.JBPopupFactory +import com.intellij.openapi.util.Condition +import com.intellij.ui.ColoredListCellRenderer +import com.intellij.ui.components.JBList +import com.intellij.util.containers.ContainerUtil +import com.intellij.util.io.socketConnection.ConnectionStatus +import io.netty.bootstrap.Bootstrap +import org.jetbrains.debugger.Vm +import org.jetbrains.io.NettyUtil +import org.jetbrains.rpc.CommandProcessor +import org.jetbrains.util.concurrency.AsyncPromise +import org.jetbrains.util.concurrency.Promise +import org.jetbrains.util.concurrency.RejectedPromise +import org.jetbrains.util.concurrency.ResolvedPromise +import java.net.ConnectException +import java.net.InetSocketAddress +import java.util.concurrent.atomic.AtomicReference + +public abstract class RemoteVmConnection : VmConnection() { + private val connectCancelHandler = AtomicReference() + + override fun getBrowser() = null + + public abstract fun createBootstrap(address: InetSocketAddress, vmResult: AsyncPromise): Bootstrap + + public fun open(address: InetSocketAddress, stopCondition: Condition? = null) { + setState(ConnectionStatus.WAITING_FOR_CONNECTION, "Connecting to ${address.getHostName()}:${address.getPort()}") + val future = ApplicationManager.getApplication().executeOnPooledThread(object : Runnable { + override fun run() { + if (Thread.interrupted()) { + return + } + + val result = AsyncPromise() + connectCancelHandler.set(Runnable { result.setError("Closed explicitly") }) + + val connectionPromise = AsyncPromise() + connectionPromise.rejected { result.setError(it) } + + result + .done { + vm = it + setState(ConnectionStatus.CONNECTED, "Connected to ${connectedAddressToPresentation(address, it)}") + startProcessing() + } + .rejected { + if (it !is ConnectException) { + Promise.logError(CommandProcessor.LOG, it) + } + setState(ConnectionStatus.CONNECTION_FAILED, it.getMessage()) + } + .processed { connectCancelHandler.set(null) } + + NettyUtil.connect(createBootstrap(address, result), address, connectionPromise, if (stopCondition == null) NettyUtil.DEFAULT_CONNECT_ATTEMPT_COUNT else -1, stopCondition) + } + }) + connectCancelHandler.set(Runnable { future.cancel(true) }) + } + + protected open fun connectedAddressToPresentation(address: InetSocketAddress, vm: Vm): String = address.getHostName() + ":" + address.getPort() + + override fun detachAndClose(): Promise<*> { + try { + connectCancelHandler.getAndSet(null)?.run() + } + finally { + return super.detachAndClose() + } + } +} + +public fun chooseDebuggee(targets: Collection, selectedIndex: Int, itemToString: (T) -> String): Promise { + if (targets.size() == 1) { + return ResolvedPromise(ContainerUtil.getFirstItem(targets)) + } + else if (targets.isEmpty()) { + return RejectedPromise("No tabs to inspect") + } + + val result = AsyncPromise() + ApplicationManager.getApplication().invokeLater(Runnable { + val list = JBList(targets) + list.setCellRenderer(object : ColoredListCellRenderer.KotlinFriendlyColoredListCellRenderer() { + override fun customizeCellRenderer(value: T, index: Int, selected: Boolean, hasFocus: Boolean) { + append(itemToString(value)) + } + }) + if (selectedIndex != -1) { + list.setSelectedIndex(selectedIndex) + } + + JBPopupFactory.getInstance() + .createListPopupBuilder(list) + .setTitle("Choose Page to Debug") + .setItemChoosenCallback(object : Runnable { + override fun run() { + @suppress("UNCHECKED_CAST") + val value = list.getSelectedValue() as T + if (value == null) { + result.setError("No target to inspect") + } + else { + result.setResult(value) + } + } + }) + .createPopup() + .showInFocusCenter() + }) + return result +} \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/VmConnection.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/VmConnection.java deleted file mode 100644 index 836857be1d98..000000000000 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/VmConnection.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.jetbrains.debugger.connection; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.util.Disposer; -import com.intellij.util.Consumer; -import com.intellij.util.EventDispatcher; -import com.intellij.util.io.socketConnection.ConnectionState; -import com.intellij.util.io.socketConnection.ConnectionStatus; -import com.intellij.util.io.socketConnection.SocketConnectionListener; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.concurrency.AsyncPromise; -import org.jetbrains.concurrency.Promise; -import org.jetbrains.debugger.DebugEventListener; -import org.jetbrains.debugger.Vm; - -import javax.swing.event.HyperlinkListener; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -public abstract class VmConnection implements Disposable, BrowserConnection { - private final AtomicReference state = new AtomicReference(new ConnectionState(ConnectionStatus.NOT_CONNECTED)); - private final EventDispatcher dispatcher = EventDispatcher.create(DebugEventListener.class); - private final EventDispatcher connectionDispatcher = EventDispatcher.create(SocketConnectionListener.class); - - protected volatile T vm; - - private final AsyncPromise opened = new AsyncPromise(); - private final AtomicBoolean closed = new AtomicBoolean(); - - public final Vm getVm() { - return vm; - } - - @NotNull - @Override - public ConnectionState getState() { - return state.get(); - } - - @SuppressWarnings("unused") - public void addDebugListener(@NotNull DebugEventListener listener, @NotNull Disposable parentDisposable) { - dispatcher.addListener(listener, parentDisposable); - } - - public void addDebugListener(@NotNull DebugEventListener listener) { - dispatcher.addListener(listener); - } - - @NotNull - public Promise opened() { - return opened; - } - - @Override - public void executeOnStart(@NotNull final Runnable runnable) { - opened.done(new Consumer() { - @Override - public void consume(Void aVoid) { - runnable.run(); - } - }); - } - - protected void setState(@NotNull ConnectionStatus status, @Nullable String message) { - setState(status, message, null); - } - - protected void setState(@NotNull ConnectionStatus status, @Nullable String message, @Nullable HyperlinkListener messageLinkListener) { - ConnectionState newState = new ConnectionState(status, message, messageLinkListener); - ConnectionState oldState = state.getAndSet(newState); - if (oldState == null || oldState.getStatus() != status) { - if (status == ConnectionStatus.CONNECTION_FAILED) { - opened.setError(Promise.createError(newState.getMessage())); - } - connectionDispatcher.getMulticaster().statusChanged(status); - } - } - - @Override - public void addListener(@NotNull SocketConnectionListener listener) { - connectionDispatcher.addListener(listener); - } - - public DebugEventListener getDebugEventListener() { - return dispatcher.getMulticaster(); - } - - protected void startProcessing() { - opened.setResult(null); - } - - public final void close(@Nullable String message, @NotNull ConnectionStatus status) { - if (!closed.compareAndSet(false, true)) { - return; - } - - if (opened.getState() == Promise.State.PENDING) { - opened.setError(Promise.createError("closed")); - } - setState(status, message); - Disposer.dispose(this, false); - } - - @Override - public void dispose() { - vm = null; - } - - @NotNull - public Promise detachAndClose() { - if (opened.getState() == Promise.State.PENDING) { - opened.setError(Promise.createError("detached and closed")); - } - - Vm currentVm = vm; - Promise callback; - if (currentVm == null) { - callback = Promise.DONE; - } - else { - vm = null; - callback = currentVm.getAttachStateManager().detach(); - } - close(null, ConnectionStatus.DISCONNECTED); - return callback; - } -} \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/VmConnection.kt b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/VmConnection.kt new file mode 100644 index 000000000000..794cbe1228d7 --- /dev/null +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/VmConnection.kt @@ -0,0 +1,114 @@ +/* + * 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.debugger.connection + +import com.intellij.openapi.Disposable +import com.intellij.openapi.util.Disposer +import com.intellij.util.EventDispatcher +import com.intellij.util.io.socketConnection.ConnectionState +import com.intellij.util.io.socketConnection.ConnectionStatus +import com.intellij.util.io.socketConnection.SocketConnectionListener +import org.jetbrains.annotations.TestOnly +import org.jetbrains.debugger.DebugEventListener +import org.jetbrains.debugger.Vm +import org.jetbrains.util.concurrency.AsyncPromise +import org.jetbrains.util.concurrency.Promise +import org.jetbrains.util.concurrency.ResolvedPromise +import org.jetbrains.util.concurrency.pending +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicReference +import javax.swing.event.HyperlinkListener + +public abstract class VmConnection : Disposable, BrowserConnection { + private val state = AtomicReference(ConnectionState(ConnectionStatus.NOT_CONNECTED)) + private val dispatcher = EventDispatcher.create(javaClass()) + private val connectionDispatcher = EventDispatcher.create(javaClass()) + + public volatile var vm: T? = null + protected set + + private val opened = AsyncPromise() + private val closed = AtomicBoolean() + + override fun getState() = state.get() + + public fun addDebugListener(listener: DebugEventListener) { + dispatcher.addListener(listener) + } + + @TestOnly + public fun opened(): Promise<*> = opened + + override fun executeOnStart(runnable: Runnable) { + opened.done { runnable.run() } + } + + protected fun setState(status: ConnectionStatus, message: String? = null, messageLinkListener: HyperlinkListener? = null) { + val newState = ConnectionState(status, message, messageLinkListener) + val oldState = state.getAndSet(newState) + if (oldState == null || oldState.getStatus() != status) { + if (status == ConnectionStatus.CONNECTION_FAILED) { + opened.setError(newState.getMessage()) + } + connectionDispatcher.getMulticaster().statusChanged(status) + } + } + + override fun addListener(listener: SocketConnectionListener) { + connectionDispatcher.addListener(listener) + } + + protected val debugEventListener: DebugEventListener + get() = dispatcher.getMulticaster() + + protected open fun startProcessing() { + opened.setResult(null) + } + + public fun close(message: String?, status: ConnectionStatus) { + if (!closed.compareAndSet(false, true)) { + return + } + + if (opened.pending) { + opened.setError("closed") + } + setState(status, message) + Disposer.dispose(this, false) + } + + override fun dispose() { + vm = null + } + + public open fun detachAndClose(): Promise<*> { + if (opened.pending) { + opened.setError("detached and closed") + } + + val currentVm = vm + val callback: Promise<*> + if (currentVm == null) { + callback = ResolvedPromise() + } + else { + vm = null + callback = currentVm.attachStateManager.detach() + } + close(null, ConnectionStatus.DISCONNECTED) + return callback + } +} \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/util.kt b/platform/script-debugger/debugger-ui/src/util.kt new file mode 100644 index 000000000000..9fea882233e0 --- /dev/null +++ b/platform/script-debugger/debugger-ui/src/util.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.xdebugger.util + +import com.intellij.xdebugger.XDebugSession +import org.jetbrains.concurrency.Promise +import org.jetbrains.debugger.SuspendContext +import org.jetbrains.debugger.Vm +import org.jetbrains.rpc.CommandProcessor +import org.jetbrains.util.concurrency.AsyncPromise + +// have to use package "com.intellij.xdebugger.util" to avoid package clash +public fun XDebugSession.rejectedErrorReporter(description: String? = null): (Throwable) -> Unit = { + Promise.logError(CommandProcessor.LOG, it) + if (it != AsyncPromise.OBSOLETE_ERROR) { + reportError("${if (description == null) "" else description + ": "}${it.getMessage()}") + } +} + +public inline fun contextDependentResultConsumer(context: SuspendContext, @inlineOptions(InlineOption.ONLY_LOCAL_RETURN) done: (result: T, vm: Vm) -> Unit) : (T) -> Unit { + return { + val vm = context.getValueManager().getVm() + if (vm.attachStateManager.isAttached() && !vm.getSuspendContextManager().isContextObsolete(context)) { + done(it, vm) + } + } +} \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/testSrc/org/jetbrains/debugger/TestCompositeNode.java b/platform/script-debugger/debugger-ui/testSrc/org/jetbrains/debugger/TestCompositeNode.java index 5690136f82a7..a41d8f7bfe7f 100644 --- a/platform/script-debugger/debugger-ui/testSrc/org/jetbrains/debugger/TestCompositeNode.java +++ b/platform/script-debugger/debugger-ui/testSrc/org/jetbrains/debugger/TestCompositeNode.java @@ -1,3 +1,18 @@ +/* + * 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.debugger; import com.intellij.openapi.util.Condition; @@ -63,7 +78,7 @@ public class TestCompositeNode implements XCompositeNode { @Override public void setErrorMessage(@NotNull String errorMessage) { - result.setError(Promise.createError(errorMessage)); + result.setError(errorMessage); } @Override diff --git a/platform/smRunner/smRunner.iml b/platform/smRunner/smRunner.iml index c36eec744bd7..d57738b3bf26 100644 --- a/platform/smRunner/smRunner.iml +++ b/platform/smRunner/smRunner.iml @@ -16,5 +16,6 @@ +
    \ No newline at end of file diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java index f79abadbd551..352573cd8f5b 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java @@ -828,8 +828,8 @@ public class SMTestRunnerResultsForm extends TestResultsPanel TestStateStorage storage = TestStateStorage.getInstance(getProject()); List tests = myRoot.getAllTests(); for (SMTestProxy proxy : tests) { - String url = proxy.getLocationUrl(); - if (proxy.isLeaf() && url != null) { + String url = proxy instanceof SMTestProxy.SMRootTestProxy ? ((SMTestProxy.SMRootTestProxy)proxy).getRootLocation() : proxy.getLocationUrl(); + if (url != null) { storage.writeState(url, new TestStateStorage.Record(proxy.getMagnitude(), new Date())); } } diff --git a/platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java b/platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java index d3507ff698d1..10983308056d 100644 --- a/platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java +++ b/platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,10 +40,15 @@ public abstract class FileStructureTestBase extends CodeInsightFixtureTestCase { @Override public void tearDown() throws Exception { - if (myPopupFixture != null) { - myPopupFixture.dispose(); + try { + if (myPopupFixture != null) { + myPopupFixture.dispose(); + myPopupFixture = null; + } + } + finally { + super.tearDown(); } - super.tearDown(); } private String getFileName(String ext) { diff --git a/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java b/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java index fa63dca8a260..02269aecb88f 100644 --- a/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java +++ b/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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. @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; import java.io.StringReader; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -38,6 +39,8 @@ public class TestLoggerFactory implements Logger.Factory { private static final String APPLICATION_MACRO = "$APPLICATION_DIR$"; private static final String LOG_DIR_MACRO = "$LOG_DIR$"; private static final String LOG_DIR = "testlog"; + private static final long LOG_SIZE_LIMIT = 100 * 1024 * 1024; + private static final long LOG_SEEK_WINDOW = 100 * 1024; private boolean myInitialized = false; @@ -84,6 +87,11 @@ public class TestLoggerFactory implements Logger.Factory { throw e; } + File ideaLog = new File(getTestLogDir(), "idea.log"); + if (ideaLog.exists() && ideaLog.length() >= LOG_SIZE_LIMIT) { + FileUtil.writeToFile(ideaLog, ""); + } + myInitialized = true; } catch (Exception e) { @@ -99,7 +107,25 @@ public class TestLoggerFactory implements Logger.Factory { File ideaLog = new File(getTestLogDir(), "idea.log"); if (ideaLog.exists()) { try { - String logText = FileUtil.loadFile(ideaLog); + long length = ideaLog.length(); + String logText; + + if (length > LOG_SEEK_WINDOW) { + RandomAccessFile file = new RandomAccessFile(ideaLog, "r"); + try { + file.seek(length - LOG_SEEK_WINDOW); + byte[] bytes = new byte[(int)LOG_SEEK_WINDOW]; + int read = file.read(bytes); + logText = new String(bytes, 0, read); + } + finally { + file.close(); + } + } + else { + logText = FileUtil.loadFile(ideaLog); + } + Pattern logStart = Pattern.compile("[0-9\\-, :\\[\\]]+(DEBUG|INFO|ERROR) - "); System.out.println("\n\nIdea Log:"); for (String line : StringUtil.splitByLines(logText.substring(Math.max(0, logText.lastIndexOf(testStartMarker))))) { diff --git a/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java b/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java index 70e655965133..bd53a43138a4 100644 --- a/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java +++ b/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java @@ -141,6 +141,8 @@ public abstract class KeymapsTestCase extends PlatformTestCase { { "meta BACK_SPACE", "EditorDeleteLine", "$Delete"}, { "control DOWN", "ShowContent", "EditorLookupDown", "MethodDown"}, { "control UP", "EditorLookupUp", "MethodUp"}, + { "control TAB", "Switcher", "Diff.FocusOppositePane"}, + { "shift control TAB", "Switcher", "Diff.FocusOppositePaneAndScroll"}, { "meta R", "Refresh", "Rerun", "Replace", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"}, { "control O", "ExportToTextFile", "OverrideMethods", }, { "control ENTER", "Generate", "NewElement"}, @@ -161,6 +163,8 @@ public abstract class KeymapsTestCase extends PlatformTestCase { { "control UP", "EditorLookupUp", "MethodUp"}, { "control ENTER", "Generate", "NewElement"}, { "control F5", "Refresh", "Rerun"}, + { "control TAB", "Switcher", "Diff.FocusOppositePane"}, + { "shift control TAB", "Switcher", "Diff.FocusOppositePaneAndScroll"}, { "meta 1", "ActivateProjectToolWindow", "FileChooser.GotoHome", "DuplicatesForm.SendToLeft"}, { "meta 2", "ActivateFavoritesToolWindow", "FileChooser.GotoProject", "DuplicatesForm.SendToRight"}, { "meta 3", "ActivateFindToolWindow", "FileChooser.GotoModule"}, @@ -170,6 +174,8 @@ public abstract class KeymapsTestCase extends PlatformTestCase { { "alt R", "Django.RunManageTaskAction", "org.jetbrains.plugins.ruby.tasks.rake.actions.RakeTasksPopupAction"}, }); put("Emacs", new String[][] { + { "ENTER", "EditorChooseLookupItem", "NextTemplateVariable", "EditorEnter", "Images.Thumbnails.EnterAction", + "PropertyInspectorActions.EditValue", "Console.Execute", "Console.TableResult.EditValue"}, { "F2", "GotoNextError", "GuiDesigner.EditComponent", "GuiDesigner.EditGroup", "Console.TableResult.EditValue"}, { "alt ENTER", "ShowIntentionActions", "Console.TableResult.EditValue", "DatabaseView.PropertiesAction"}, { "TAB", "EditorChooseLookupItemReplace", "NextTemplateVariable", "NextParameter", "EditorIndentSelection", @@ -181,6 +187,7 @@ public abstract class KeymapsTestCase extends PlatformTestCase { { "control 3", "GotoBookmark3", "FileChooser.GotoModule"}, { "control D", "$Delete", "Diff.ShowDiff", "CompareTwoFiles", "SendEOF", "FileChooser.GotoDesktop"}, { "control K", "EditorCutLineEnd", "CheckinProject"}, + { "control M", "EditorEnter", "EditorChooseLookupItem", "NextTemplateVariable", "Console.Execute"}, { "control N", "EditorDown", "FileChooser.NewFolder"}, { "control P", "EditorUp", "FileChooser.TogglePathShowing"}, { "control R", "Console.TableResult.Reload", "org.jetbrains.plugins.ruby.rails.console.ReloadSources", "FindPrevious"}, diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/FixtureRule.kt b/platform/testFramework/testSrc/com/intellij/testFramework/FixtureRule.kt index 492a4954872d..e8852af9b643 100644 --- a/platform/testFramework/testSrc/com/intellij/testFramework/FixtureRule.kt +++ b/platform/testFramework/testSrc/com/intellij/testFramework/FixtureRule.kt @@ -118,7 +118,7 @@ public class ProjectRule() : ExternalResource() { } if (projectOpened.compareAndSet(false, true)) { - ProjectManagerEx.getInstanceEx().openTestProject(project) + runInEdtAndWait { ProjectManagerEx.getInstanceEx().openTestProject(project) } } return result!! } @@ -128,15 +128,17 @@ public class ProjectRule() : ExternalResource() { var project = project var result = sharedModule if (result == null) { - LightProjectDescriptor().setUpProject(project, object: LightProjectDescriptor.SetupHandler { - override fun moduleCreated(module: Module) { - result = module - sharedModule = module - } + runInEdtAndWait { + LightProjectDescriptor().setUpProject(project, object : LightProjectDescriptor.SetupHandler { + override fun moduleCreated(module: Module) { + result = module + sharedModule = module + } - override fun sourceRootCreated(sourceRoot: VirtualFile) { - } - }) + override fun sourceRootCreated(sourceRoot: VirtualFile) { + } + }) + } } return result!! } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/CompositePrintable.java b/platform/testRunner/src/com/intellij/execution/testframework/CompositePrintable.java index 9ab852bfca67..fbb0602b7c32 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/CompositePrintable.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/CompositePrintable.java @@ -78,7 +78,7 @@ public class CompositePrintable implements Printable, Disposable { printables.add(printable); } } - myWrapper.printOn(printer, printables); + myWrapper.printOn(printer, printables, true); } public void addLast(@NotNull final Printable printable) { @@ -224,9 +224,17 @@ public class CompositePrintable implements Printable, Disposable { } public void printOn(final Printer console, final List printables) { + printOn(console, printables, false); + } + + public void printOn(final Printer console, final List printables, final boolean skipFileContent) { final Runnable request = new Runnable() { @Override public void run() { + if (skipFileContent) { + readFileContentAndPrint(console, null, printables); + return; + } final File file = hasOutput() ? getFile() : null; synchronized (myFileLock) { readFileContentAndPrint(console, file, printables); diff --git a/platform/testRunner/src/com/intellij/execution/testframework/actions/TestDiffRequestProcessor.java b/platform/testRunner/src/com/intellij/execution/testframework/actions/TestDiffRequestProcessor.java index aff16950c712..9e8aeb14e34e 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/actions/TestDiffRequestProcessor.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/actions/TestDiffRequestProcessor.java @@ -86,7 +86,7 @@ public class TestDiffRequestProcessor extends DiffRequestProcessor { } else { title = ExecutionBundle.message(titleKey); - content = DiffContentFactory.getInstance().create(contentString, null); + content = DiffContentFactory.getInstance().create(contentString); } return Pair.create(title, content); } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java b/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java index 21b1dd767a39..e6a4a752797f 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java @@ -19,6 +19,7 @@ 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.ConsoleBuffer; import com.intellij.execution.impl.RunManagerImpl; import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl; import com.intellij.execution.testframework.*; @@ -237,9 +238,11 @@ public class TestResultsXmlFormatter { final Ref lastType = new Ref(); final Ref error = new Ref(); + final int bufferSize = ConsoleBuffer.useCycleBuffer() ? ConsoleBuffer.getCycleBufferSize() : -1; final Printer printer = new Printer() { @Override public void print(String text, ConsoleViewContentType contentType) { + ProgressManager.checkCanceled(); if (contentType != lastType.get()) { if (buffer.length() > 0) { try { @@ -251,7 +254,9 @@ public class TestResultsXmlFormatter { } lastType.set(contentType); } - buffer.append(text); + if (bufferSize < 0 || buffer.length() < bufferSize) { + buffer.append(text); + } } @Override diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java index 5d3aa9da061d..9b5cfb4c26a0 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java @@ -25,7 +25,6 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; @@ -76,7 +75,6 @@ import javax.swing.plaf.TreeUI; import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.*; import java.awt.*; -import java.awt.datatransfer.StringSelection; import java.awt.event.*; import java.util.*; import java.util.List; @@ -1474,8 +1472,9 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra return myModel.areTargetsValid(); } - private class MyPanel extends JPanel implements TypeSafeDataProvider, OccurenceNavigator,Disposable, CopyProvider { + private class MyPanel extends JPanel implements TypeSafeDataProvider, OccurenceNavigator,Disposable{ @Nullable private OccurenceNavigatorSupport mySupport; + private CopyProvider myCopyProvider; private MyPanel(@NotNull JTree tree) { mySupport = new OccurenceNavigatorSupport(tree) { @@ -1496,6 +1495,21 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra return UsageViewBundle.message("action.previous.occurrence"); } }; + myCopyProvider = new TextCopyProvider() { + @Nullable + @Override + public Collection getTextLinesToCopy() { + final Node[] selectedNodes = getSelectedNodes(); + if (selectedNodes != null && selectedNodes.length > 0) { + ArrayList lines = ContainerUtil.newArrayList(); + for (Node node : selectedNodes) { + lines.add(node.getText(UsageViewImpl.this)); + } + return lines; + } + return null; + } + }; } @Override @@ -1533,24 +1547,6 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra return mySupport != null ? mySupport.getPreviousOccurenceActionName() : ""; } - @Override - public void performCopy(@NotNull DataContext dataContext) { - final Node selectedNode = getSelectedNode(); - assert selectedNode != null; - final String plainText = selectedNode.getText(UsageViewImpl.this); - CopyPasteManager.getInstance().setContents(new StringSelection(plainText.trim())); - } - - @Override - public boolean isCopyEnabled(@NotNull DataContext dataContext) { - return getSelectedNode() != null; - } - - @Override - public boolean isCopyVisible(@NotNull DataContext dataContext) { - return true; - } - @Override public void calcData(final DataKey key, final DataSink sink) { Node node = getSelectedNode(); @@ -1590,7 +1586,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra sink.put(PlatformDataKeys.HELP_ID, HELP_ID); } else if (key == PlatformDataKeys.COPY_PROVIDER) { - sink.put(PlatformDataKeys.COPY_PROVIDER, this); + sink.put(PlatformDataKeys.COPY_PROVIDER, myCopyProvider); } else if (node != null) { Object userObject = node.getUserObject(); diff --git a/platform/util-rt/src/com/intellij/openapi/util/io/FileUtilRt.java b/platform/util-rt/src/com/intellij/openapi/util/io/FileUtilRt.java index 8b76f8008cd1..8a48ba176218 100644 --- a/platform/util-rt/src/com/intellij/openapi/util/io/FileUtilRt.java +++ b/platform/util-rt/src/com/intellij/openapi/util/io/FileUtilRt.java @@ -617,13 +617,7 @@ public class FileUtilRt { @NotNull public static byte[] loadBytes(@NotNull InputStream stream) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - final byte[] bytes = BUFFER.get(); - while (true) { - int n = stream.read(bytes, 0, bytes.length); - if (n <= 0) break; - buffer.write(bytes, 0, n); - } - buffer.close(); + copy(stream, buffer); return buffer.toByteArray(); } @@ -678,7 +672,7 @@ public class FileUtilRt { /** * Warning! this method is _not_ symlinks-aware. Consider using com.intellij.openapi.util.io.FileUtil.delete() * @param file file or directory to delete - * @return true if the file did not exist or was successfully deleted + * @return true if the file did not exist or was successfully deleted */ public static boolean delete(@NotNull File file) { if (NIOReflect.IS_AVAILABLE) { @@ -696,7 +690,7 @@ public class FileUtilRt { Files.deleteIfExists(file); return FileVisitResult.CONTINUE; } - + @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.deleteIfExists(dir); @@ -720,7 +714,7 @@ public class FileUtilRt { } return !file.exists(); } - + private static boolean deleteRecursively(@NotNull File file) { File[] files = file.listFiles(); if (files != null) { @@ -843,7 +837,7 @@ public class FileUtilRt { } } else { - final byte[] buffer = BUFFER.get(); + final byte[] buffer = getThreadLocalBuffer(); while (true) { int read = inputStream.read(buffer); if (read < 0) break; @@ -852,6 +846,12 @@ public class FileUtilRt { } } + @NotNull + public static byte[] getThreadLocalBuffer() { + return BUFFER.get(); + } + + public static int getUserFileSizeLimit() { try { return Integer.parseInt(System.getProperty("idea.max.intellisense.filesize")) * KILOBYTE; diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 9bbbe1a58742..85efd2301fbd 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -237,6 +237,7 @@ vcs.showConsole=true vcs.log.bek.sort=true vcs.log.bek.sort.disabled=false vcs.log.linear.bek.sort=false +vcs.log.square.labels=false vcs.executable.validator.timeout.sec=60 @@ -652,4 +653,8 @@ ruby.remote.debugger.supports.catchpoint.removal=true use.read.action.to.init.service=true -ide.mac.new.color.picker=false \ No newline at end of file +ide.mac.new.color.picker=false + +trace.clipboard.events=false +trace.clipboard.events.description=Enable detection and logging of OS notifications about external clipboard changes +trace.clipboard.events.restartRequired=true \ No newline at end of file diff --git a/platform/util/src/com/intellij/icons/AllIcons.java b/platform/util/src/com/intellij/icons/AllIcons.java index b82a6115fa06..e4206f28a18c 100644 --- a/platform/util/src/com/intellij/icons/AllIcons.java +++ b/platform/util/src/com/intellij/icons/AllIcons.java @@ -1039,6 +1039,7 @@ public class AllIcons { public static final Icon Oracle = IconLoader.getIcon("/providers/oracle.png"); // 16x16 public static final Icon Postgresql = IconLoader.getIcon("/providers/postgresql.png"); // 16x16 public static final Icon Sqlite = IconLoader.getIcon("/providers/sqlite.png"); // 16x16 + public static final Icon SqlServer = IconLoader.getIcon("/providers/sqlServer.png"); // 16x16 public static final Icon Sun = IconLoader.getIcon("/providers/sun.png"); // 16x16 public static final Icon Sybase = IconLoader.getIcon("/providers/sybase.png"); // 16x16 diff --git a/platform/util/src/com/intellij/openapi/diagnostic/FrequentEventDetector.java b/platform/util/src/com/intellij/openapi/diagnostic/FrequentEventDetector.java index 7f28c8f73187..7a9062621658 100644 --- a/platform/util/src/com/intellij/openapi/diagnostic/FrequentEventDetector.java +++ b/platform/util/src/com/intellij/openapi/diagnostic/FrequentEventDetector.java @@ -18,6 +18,8 @@ package com.intellij.openapi.diagnostic; import com.intellij.util.ExceptionUtil; import org.jetbrains.annotations.NotNull; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** @@ -30,6 +32,12 @@ public class FrequentEventDetector { private long myStartedCounting = System.currentTimeMillis(); private final AtomicInteger myEventsPosted = new AtomicInteger(); + private final Map myRecentTraces = new LinkedHashMap() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > 5; + } + }; private final int myEventCountThreshold; private final int myTimeSpanMs; private final Level myLevel; @@ -58,7 +66,13 @@ public class FrequentEventDetector { } if (shouldLog) { - String message = "Too many events posted\n" + ExceptionUtil.getThrowableText(new Throwable()); + String trace = ExceptionUtil.getThrowableText(new Throwable()); + boolean logTrace; + synchronized (myEventsPosted) { + logTrace = myRecentTraces.put(trace, trace) == null; + } + + String message = "Too many events posted" + (logTrace ? "\n" + trace : ""); if (myLevel == Level.INFO) { LOG.info(message); } diff --git a/platform/util/src/com/intellij/openapi/util/BuildNumber.java b/platform/util/src/com/intellij/openapi/util/BuildNumber.java index 3b2fc340740c..c720b7fbda95 100644 --- a/platform/util/src/com/intellij/openapi/util/BuildNumber.java +++ b/platform/util/src/com/intellij/openapi/util/BuildNumber.java @@ -158,15 +158,14 @@ public class BuildNumber implements Comparable { private static BuildNumber fromFile() { try { - final String homePath = PathManager.getHomePath(); - final File buildTxtFile = FileUtil.findFirstThatExist(homePath + "/build.txt", homePath + "/community/build.txt"); + String home = PathManager.getHomePath(); + File buildTxtFile = FileUtil.findFirstThatExist(home + "/build.txt", home + "/Resources/build.txt", home + "/community/build.txt"); if (buildTxtFile != null) { String text = FileUtil.loadFile(buildTxtFile).trim(); return fromString(text); } } - catch (IOException ignored) { - } + catch (IOException ignored) { } return fallback(); } diff --git a/platform/util/src/com/intellij/openapi/util/JDOMExternalizableStringList.java b/platform/util/src/com/intellij/openapi/util/JDOMExternalizableStringList.java index 44c9f8317880..969f8a081df4 100644 --- a/platform/util/src/com/intellij/openapi/util/JDOMExternalizableStringList.java +++ b/platform/util/src/com/intellij/openapi/util/JDOMExternalizableStringList.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package com.intellij.openapi.util; import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.ReflectionUtil; import org.jdom.Element; +import org.jdom.IllegalDataException; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -47,7 +48,7 @@ public class JDOMExternalizableStringList extends ArrayList implements J } @Override - public void readExternal(Element element) throws InvalidDataException { + public void readExternal(Element element) { clear(); Class callerClass = null; @@ -59,8 +60,7 @@ public class JDOMExternalizableStringList extends ArrayList implements J final ClassLoader classLoader = callerClass.getClassLoader(); for (Element listItemElement : listElement.getChildren(ATTR_ITEM)) { if (!ATTR_ITEM.equals(listItemElement.getName())) { - throw new InvalidDataException( - "Unable to read list item. Unknown element found: " + listItemElement.getName()); + throw new IllegalDataException("Unable to read list item. Unknown element found: " + listItemElement.getName()); } String itemClassString = listItemElement.getAttributeValue(ATTR_CLASS); Class itemClass; @@ -68,8 +68,7 @@ public class JDOMExternalizableStringList extends ArrayList implements J itemClass = Class.forName(itemClassString, true, classLoader); } catch (ClassNotFoundException ex) { - throw new InvalidDataException( - "Unable to read list item: unable to load class: " + itemClassString + " \n" + ex.getMessage()); + throw new IllegalDataException("Unable to read list item: unable to load class: " + itemClassString + " \n" + ex.getMessage()); } String listItem = listItemElement.getAttributeValue(ATTR_VALUE); diff --git a/platform/util/src/com/intellij/openapi/util/JDOMUtil.java b/platform/util/src/com/intellij/openapi/util/JDOMUtil.java index ce2acdafdc93..edd24378e9e9 100644 --- a/platform/util/src/com/intellij/openapi/util/JDOMUtil.java +++ b/platform/util/src/com/intellij/openapi/util/JDOMUtil.java @@ -354,7 +354,7 @@ public class JDOMUtil { } @NotNull - public static Document loadDocument(URL url) throws JDOMException, IOException { + public static Document loadDocument(@NotNull URL url) throws JDOMException, IOException { return loadDocument(URLUtil.openStream(url)); } diff --git a/platform/util/src/com/intellij/openapi/util/SystemInfo.java b/platform/util/src/com/intellij/openapi/util/SystemInfo.java index 84c2aec7a4f8..d349bd19dada 100644 --- a/platform/util/src/com/intellij/openapi/util/SystemInfo.java +++ b/platform/util/src/com/intellij/openapi/util/SystemInfo.java @@ -138,6 +138,7 @@ public class SystemInfo extends SystemInfoRt { public static final boolean isMacOSMountainLion = isMac && isOsVersionAtLeast("10.8"); public static final boolean isMacOSMavericks = isMac && isOsVersionAtLeast("10.9"); public static final boolean isMacOSYosemite = isMac && isOsVersionAtLeast("10.10"); + public static final boolean isMacOSElCapitan = isMac && isOsVersionAtLeast("10.11"); @NotNull public static String getMacOSMajorVersion() { diff --git a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java index d980bf0eac7d..5a1b2106ac44 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -30,7 +30,10 @@ import com.intellij.util.text.FilePathHashingStrategy; import com.intellij.util.text.StringFactory; import gnu.trove.TObjectHashingStrategy; import org.intellij.lang.annotations.RegExp; -import org.jetbrains.annotations.*; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import java.io.*; import java.lang.reflect.Method; @@ -232,27 +235,10 @@ public class FileUtil extends FileUtilRt { return bytes; } - public static boolean processFirstBytes(@NotNull InputStream stream, int length, @NotNull Processor processor) - throws IOException { - final byte[] bytes = BUFFER.get(); - assert bytes.length >= length : "Cannot process more than " + bytes.length + " in one call, requested:" + length; - - int n = stream.read(bytes, 0, length); - if (n <= 0) return false; - - return processor.process(new ByteSequence(bytes, 0, n)); - } - @NotNull public static byte[] loadFirst(@NotNull InputStream stream, int maxLength) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - final byte[] bytes = BUFFER.get(); - while (maxLength > 0) { - int n = stream.read(bytes, 0, Math.min(maxLength, bytes.length)); - if (n <= 0) break; - buffer.write(bytes, 0, n); - maxLength -= n; - } + copy(stream, maxLength, buffer); buffer.close(); return buffer.toByteArray(); } @@ -308,7 +294,7 @@ public class FileUtil extends FileUtilRt { @NotNull public static byte[] adaptiveLoadBytes(@NotNull InputStream stream) throws IOException { - byte[] bytes = new byte[4096]; + byte[] bytes = getThreadLocalBuffer(); List buffers = null; int count = 0; int total = 0; @@ -532,7 +518,7 @@ public class FileUtil extends FileUtilRt { } public static void copy(@NotNull InputStream inputStream, int maxSize, @NotNull OutputStream outputStream) throws IOException { - final byte[] buffer = BUFFER.get(); + final byte[] buffer = getThreadLocalBuffer(); int toRead = maxSize; while (toRead > 0) { int read = inputStream.read(buffer, 0, Math.min(buffer.length, toRead)); diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index 7748c3e26404..998a7c8143c1 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -58,6 +58,7 @@ public class StringUtil extends StringUtilRt { new ParserDelegator().parse(in, this, Boolean.TRUE); } + @Override public void handleText(char[] text, int pos) { if (myBuffer.length() > 0) myBuffer.append(SystemProperties.getLineSeparator()); @@ -67,9 +68,9 @@ public class StringUtil extends StringUtilRt { public String getText() { return myBuffer.toString(); } - }; + } - private static MyHtml2Text html2TextParser = new MyHtml2Text(); + private static final MyHtml2Text html2TextParser = new MyHtml2Text(); public static final NotNullFunction QUOTER = new NotNullFunction() { @Override @@ -499,12 +500,7 @@ public class StringUtil extends StringUtilRt { } } } - if (buffer == null) { - return s; - } - else { - return buffer.toString(); - } + return buffer == null ? s : buffer.toString(); } @NonNls private static final String[] ourPrepositions = { @@ -524,7 +520,7 @@ public class StringUtil extends StringUtilRt { if (lastChar - firstChar + 1 == preposition.length()) { found = true; for (int j = 0; j < preposition.length(); j++) { - if (!(toLowerCase(s.charAt(firstChar + j)) == preposition.charAt(j))) { + if (toLowerCase(s.charAt(firstChar + j)) != preposition.charAt(j)) { found = false; } } @@ -786,7 +782,7 @@ public class StringUtil extends StringUtilRt { if (escaped) buffer.append('\\'); } - @SuppressWarnings({"HardCodedStringLiteral"}) + @SuppressWarnings("HardCodedStringLiteral") @NotNull @Contract(pure = true) public static String pluralize(@NotNull String suggestion) { @@ -1140,7 +1136,7 @@ public class StringUtil extends StringUtilRt { @Contract(value = "null -> true",pure = true) // we need to keep this method to preserve backward compatibility public static boolean isEmptyOrSpaces(@Nullable String s) { - return isEmptyOrSpaces(((CharSequence)s)); + return isEmptyOrSpaces((CharSequence)s); } @Contract(value = "null -> true", pure = true) @@ -1601,7 +1597,7 @@ public class StringUtil extends StringUtilRt { * @param name english word in plural form * @return name in singular form or null if failed to find one. */ - @SuppressWarnings({"HardCodedStringLiteral"}) + @SuppressWarnings("HardCodedStringLiteral") @Nullable @Contract(pure = true) public static String unpluralize(@NotNull final String name) { @@ -2324,15 +2320,13 @@ public class StringUtil extends StringUtilRt { if (methodName.startsWith("get")) { return Introspector.decapitalize(methodName.substring(3)); } - else if (methodName.startsWith("is")) { + if (methodName.startsWith("is")) { return Introspector.decapitalize(methodName.substring(2)); } - else if (methodName.startsWith("set")) { + if (methodName.startsWith("set")) { return Introspector.decapitalize(methodName.substring(3)); } - else { - return null; - } + return null; } @Contract(pure = true) @@ -2342,7 +2336,7 @@ public class StringUtil extends StringUtilRt { @Contract(pure = true) public static boolean isJavaIdentifierPart(char c) { - return c >= '0' && c <= '9' || isJavaIdentifierStart(c); + return c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || Character.isJavaIdentifierPart(c); } @Contract(pure = true) @@ -2739,7 +2733,7 @@ public class StringUtil extends StringUtilRt { if (i < string1Length) { return 1; } - else if (j < string2Length) { + if (j < string2Length) { return -1; } if (!caseSensitive && string1Length == string2Length) { @@ -3249,7 +3243,7 @@ public class StringUtil extends StringUtilRt { */ public abstract static class BombedCharSequence implements CharSequence { private final CharSequence delegate; - private int i = 0; + private int i; public BombedCharSequence(@NotNull CharSequence sequence) { delegate = sequence; @@ -3282,6 +3276,7 @@ public class StringUtil extends StringUtilRt { protected abstract void checkCanceled(); + @NotNull @Override public CharSequence subSequence(int i, int i1) { check(); diff --git a/platform/util/src/com/intellij/util/containers/ConcurrentPackedBitsArray.java b/platform/util/src/com/intellij/util/containers/ConcurrentPackedBitsArray.java index f7facc262c3b..17757ee73818 100644 --- a/platform/util/src/com/intellij/util/containers/ConcurrentPackedBitsArray.java +++ b/platform/util/src/com/intellij/util/containers/ConcurrentPackedBitsArray.java @@ -42,6 +42,7 @@ public class ConcurrentPackedBitsArray { * The returned bits are LSB, other (64-bitsPerChunk) higher bits are undefined */ public long get(int id) { + assert id >= 0 : id; int bitIndex = id/chunksPerWord * 64 + (id%chunksPerWord)*bitsPerChunk; long word = bits.getWord(bitIndex) >> bitIndex; return word; @@ -49,6 +50,7 @@ public class ConcurrentPackedBitsArray { // stores chunk atomically, returns previous chunk public long set(int id, final long flags) { + assert id >= 0 : id; if ((flags & ~mask) != 0) { throw new IllegalArgumentException("Flags must be between 0 and "+ mask +" but got:"+flags); } diff --git a/platform/util/src/com/intellij/util/containers/FilteredTraverserBase.java b/platform/util/src/com/intellij/util/containers/FilteredTraverserBase.java index 86c31037247c..6b721e211ecb 100644 --- a/platform/util/src/com/intellij/util/containers/FilteredTraverserBase.java +++ b/platform/util/src/com/intellij/util/containers/FilteredTraverserBase.java @@ -22,7 +22,6 @@ import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -108,8 +107,8 @@ public abstract class FilteredTraverserBase> NOT_IN_HIERARCHY_COMPONENTS = Key.create("NOT_IN_HIERARCHY_COMPONENTS"); + + private static final boolean LEGACY_JDK = !SystemInfo.isJavaVersionAtLeast("1.8"); /** * Replaces SwingUtilities#isLeftMouseButton() for consistency with other button-related methods * - * @see javax.swing.SwingUtilities#isLeftMouseButton(java.awt.event.MouseEvent) + * @see SwingUtilities#isLeftMouseButton(MouseEvent) */ public static boolean isLeftMouseButton(MouseEvent anEvent) { - return (anEvent.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) > 0; + return LEGACY_JDK ? (anEvent.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) > 0 : SwingUtilities.isLeftMouseButton(anEvent); } /** * Replaces SwingUtilities#isMiddleMouseButton() due to the fact that BUTTON2_MASK == Event.ALT_MASK * - * @see javax.swing.SwingUtilities#isMiddleMouseButton(java.awt.event.MouseEvent) - * @see java.awt.event.InputEvent#BUTTON2_MASK + * @see SwingUtilities#isMiddleMouseButton(MouseEvent) + * @see InputEvent#BUTTON2_MASK */ public static boolean isMiddleMouseButton(MouseEvent anEvent) { - return (anEvent.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK) > 0; + return LEGACY_JDK ? (anEvent.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK) > 0 : SwingUtilities.isMiddleMouseButton(anEvent); } /** * Replaces SwingUtilities#isRightMouseButton() due to the fact that BUTTON3_MASK == Event.META_MASK * - * @see javax.swing.SwingUtilities#isRightMouseButton(java.awt.event.MouseEvent) - * @see java.awt.event.InputEvent#BUTTON3_MASK + * @see SwingUtilities#isRightMouseButton(MouseEvent) + * @see InputEvent#BUTTON3_MASK */ public static boolean isRightMouseButton(MouseEvent anEvent) { - return (anEvent.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) > 0; + return LEGACY_JDK ? (anEvent.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) > 0 : SwingUtilities.isRightMouseButton(anEvent); } @NotNull diff --git a/platform/util/src/com/intellij/util/xmlb/XmlSerializer.java b/platform/util/src/com/intellij/util/xmlb/XmlSerializer.java index 41a3e86a5a43..e13c122d9aa3 100644 --- a/platform/util/src/com/intellij/util/xmlb/XmlSerializer.java +++ b/platform/util/src/com/intellij/util/xmlb/XmlSerializer.java @@ -87,7 +87,7 @@ public class XmlSerializer { } @Nullable - public static T deserialize(URL url, Class aClass) throws XmlSerializationException { + public static T deserialize(@NotNull URL url, Class aClass) throws XmlSerializationException { try { Document document = JDOMUtil.loadDocument(url); document = JDOMXIncluder.resolve(document, url.toExternalForm()); diff --git a/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java b/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java index 6af19a615746..19f32835f23a 100644 --- a/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java +++ b/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java @@ -294,7 +294,7 @@ public class StringUtilTest { @Test public void testReplace() { - assertEquals(StringUtil.replace("$PROJECT_FILE$/filename", "$PROJECT_FILE$", "/tmp"), "/tmp/filename"); + assertEquals("/tmp/filename", StringUtil.replace("$PROJECT_FILE$/filename", "$PROJECT_FILE$", "/tmp")); } @Test diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java index 3a2a8ad5aefb..dfc3cf7c99e8 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java @@ -22,15 +22,10 @@ */ package com.intellij.openapi.vcs.changes.patch; -import com.intellij.diff.*; -import com.intellij.diff.chains.DiffRequestProducerException; -import com.intellij.diff.contents.DiffContent; -import com.intellij.diff.contents.DocumentContent; +import com.intellij.diff.DiffManager; +import com.intellij.diff.InvalidDiffRequestException; import com.intellij.diff.merge.MergeRequest; import com.intellij.diff.merge.MergeResult; -import com.intellij.diff.requests.DiffRequest; -import com.intellij.diff.requests.SimpleDiffRequest; -import com.intellij.diff.util.DiffUtil; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -47,19 +42,17 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.WindowWrapper; -import com.intellij.openapi.util.BooleanGetter; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsApplicationSettings; import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.CommitContext; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.EditorNotificationPanel; import com.intellij.util.Consumer; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; @@ -163,11 +156,52 @@ public class ApplyPatchAction extends DumbAwareAction { final ApplyPatchForBaseRevisionTexts mergeData = result.getMergeData(); if (mergeData == null) return status; - if (mergeData.getBase() != null) { - return showMergeDialog(project, file, mergeData.getBase(), mergeData.getPatched(), reverse, leftPanelTitle, rightPanelTitle); + final Document document = FileDocumentManager.getInstance().getDocument(file); + if (document == null) return ApplyPatchStatus.FAILURE; + + String baseContent = toString(mergeData.getBase()); + String localContent = toString(mergeData.getLocal()); + String patchedContent = mergeData.getPatched(); + + if (localContent == null) return ApplyPatchStatus.FAILURE; + + final Ref successRef = new Ref(); + Consumer callback = new Consumer() { + @Override + public void consume(MergeResult result) { + FileDocumentManager.getInstance().saveDocument(document); + successRef.set(result != MergeResult.CANCEL); + } + }; + + try { + MergeRequest request; + if (baseContent != null) { + if (reverse) { + if (leftPanelTitle == null) leftPanelTitle = VcsBundle.message("patch.apply.conflict.patched.version"); + if (rightPanelTitle == null) rightPanelTitle = VcsBundle.message("patch.apply.conflict.local.version"); + + List contents = ContainerUtil.list(patchedContent, baseContent, localContent); + List titles = ContainerUtil.list(leftPanelTitle, null, rightPanelTitle); + + request = PatchDiffRequestFactory + .createMergeRequest(project, document, file, contents, null, titles, callback); + } + else { + request = PatchDiffRequestFactory + .createMergeRequest(project, document, file, baseContent, localContent, patchedContent, callback); + } + } + else { + request = PatchDiffRequestFactory.createBadMergeRequest(project, document, file, localContent, patchedContent, callback); + } + + DiffManager.getInstance().showMerge(project, request); + return successRef.get() == Boolean.TRUE ? ApplyPatchStatus.SUCCESS : ApplyPatchStatus.FAILURE; } - else { - return showBadDiffDialog(project, file, mergeData); + catch (InvalidDiffRequestException e) { + LOG.warn(e); + return ApplyPatchStatus.FAILURE; } } @@ -201,135 +235,8 @@ public class ApplyPatchAction extends DumbAwareAction { }); } - @NotNull - private static ApplyPatchStatus showMergeDialog(@Nullable Project project, - @NotNull VirtualFile file, - @Nullable CharSequence content, - @NotNull final String patchedContent, - boolean reverse, - @Nullable String leftPanelTitle, - @Nullable String rightPanelTitle) { - Document document = FileDocumentManager.getInstance().getDocument(file); - if (content == null || document == null) { - return ApplyPatchStatus.FAILURE; - } - - List titles = ContainerUtil.list( - leftPanelTitle == null ? VcsBundle.message("patch.apply.conflict.local.version") : leftPanelTitle, - rightPanelTitle == null ? VcsBundle.message("patch.apply.conflict.merged.version") : rightPanelTitle, - VcsBundle.message("patch.apply.conflict.patched.version")); - String windowTitle = VcsBundle.message("patch.apply.conflict.title", file.getPresentableUrl()); - - final String leftText = document.getText(); - List contents = ContainerUtil.list(reverse ? patchedContent : leftText, - content.toString(), - reverse ? leftText : patchedContent); - - final Ref successRef = new Ref(); - final Consumer callback = new Consumer() { - @Override - public void consume(MergeResult result) { - successRef.set(result != MergeResult.CANCEL); - } - }; - - try { - MergeRequest request = DiffRequestFactory.getInstance() - .createMergeRequest(project, file.getFileType(), document, contents, windowTitle, titles, callback); - - DiffManager.getInstance().showMerge(project, request); - - return successRef.get() == Boolean.TRUE ? ApplyPatchStatus.SUCCESS : ApplyPatchStatus.FAILURE; - } - catch (InvalidDiffRequestException e) { - LOG.warn(e); - return ApplyPatchStatus.FAILURE; - } - } - - @NotNull - private static ApplyPatchStatus showBadDiffDialog(@Nullable Project project, - @NotNull VirtualFile file, - @NotNull final ApplyPatchForBaseRevisionTexts texts) { - final Document document = FileDocumentManager.getInstance().getDocument(file); - if (texts.getLocal() == null || document == null) return ApplyPatchStatus.FAILURE; - - final CharSequence oldContent = document.getImmutableCharSequence(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - document.setText(texts.getPatched()); - } - }); - - final String fullPath = file.getParent() == null ? file.getPath() : file.getParent().getPath(); - final String windowTitle = "Result Of Patch Apply To " + file.getName() + " (" + fullPath + ")"; - final List titles = ContainerUtil.list(VcsBundle.message("diff.title.local"), "Patched (with problems)"); - - final DiffContentFactory contentFactory = DiffContentFactory.getInstance(); - final DocumentContent originalContent = contentFactory.create(texts.getLocal().toString(), file.getFileType()); - final DiffContent mergedContent = contentFactory.create(project, document); - - final List contents = ContainerUtil.list(originalContent, mergedContent); - - final DiffRequest request = new SimpleDiffRequest(windowTitle, contents, titles); - DiffUtil.addNotification(new DiffIsApproximateNotification(), request); - - final DiffDialogHints dialogHints = new DiffDialogHints(WindowWrapper.Mode.MODAL); - dialogHints.setCancelAction(new BooleanGetter() { - @Override - public boolean get() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - document.setText(oldContent); - FileDocumentManager.getInstance().saveDocument(document); - } - }); - return true; - } - }); - dialogHints.setOkAction(new BooleanGetter() { - @Override - public boolean get() { - FileDocumentManager.getInstance().saveDocument(document); - return true; - } - }); - - DiffManager.getInstance().showDiff(project, request, dialogHints); - - return ApplyPatchStatus.SUCCESS; - } - - @NotNull - public static DiffRequest createBadDiffRequest(@Nullable Project project, - @NotNull final VirtualFile file, - @NotNull final ApplyPatchForBaseRevisionTexts texts) throws DiffRequestProducerException { - if (texts.getLocal() == null) { - throw new DiffRequestProducerException("Can't show diff for '" + file.getPresentableUrl() + "'"); - } - - final String fullPath = file.getParent() == null ? file.getPath() : file.getParent().getPath(); - final String windowTitle = "Result Of Patch Apply To " + file.getName() + " (" + fullPath + ")"; - final List titles = ContainerUtil.list(VcsBundle.message("diff.title.local"), "Patched (with problems)"); - - final DiffContentFactory contentFactory = DiffContentFactory.getInstance(); - DocumentContent localContent = contentFactory.createDocument(project, file); - if (localContent == null) localContent = contentFactory.create(texts.getLocal().toString(), file.getFileType()); - final DiffContent mergedContent = contentFactory.create(texts.getPatched(), file.getFileType()); - - final List contents = ContainerUtil.list(localContent, mergedContent); - - final DiffRequest request = new SimpleDiffRequest(windowTitle, contents, titles); - DiffUtil.addNotification(new DiffIsApproximateNotification(), request); - - return request; - } - - private static class DiffIsApproximateNotification extends EditorNotificationPanel { - public DiffIsApproximateNotification() { - myLabel.setText("Couldn't find context for patch. Some fragments were applied at the best possible place. Please check carefully."); - } + @Nullable + private static String toString(@Nullable CharSequence charSequence) { + return charSequence != null ? StringUtil.convertLineSeparators(charSequence.toString()) : null; } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchMergeRequest.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchMergeRequest.java new file mode 100644 index 000000000000..63cb798a746f --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchMergeRequest.java @@ -0,0 +1,138 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.changes.patch; + +import com.intellij.diff.merge.MergeRequest; +import com.intellij.diff.merge.MergeResult; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ApplyPatchMergeRequest extends MergeRequest { + @Nullable private final Project myProject; + @NotNull private final Document myDocument; + + @NotNull private final CharSequence myOriginalContent; + @NotNull private final String myLocalContent; + @NotNull private final String myPatchedContent; + + @NotNull private final String myWindowTitle; + @NotNull private final String myLocalTitle; + @NotNull private final String myPatchedTitle; + + @Nullable private final Consumer myCallback; + + public ApplyPatchMergeRequest(@Nullable Project project, + @NotNull Document document, + @NotNull String localContent, + @NotNull String patchedContent, + @NotNull String windowTitle, + @NotNull String localTitle, + @NotNull String patchedTitle, + @Nullable Consumer callback) { + myProject = project; + myDocument = document; + + myOriginalContent = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public CharSequence compute() { + return myDocument.getImmutableCharSequence(); + } + }); + myLocalContent = localContent; + myPatchedContent = patchedContent; + + myWindowTitle = windowTitle; + myLocalTitle = localTitle; + myPatchedTitle = patchedTitle; + + myCallback = callback; + } + + @Nullable + public Project getProject() { + return myProject; + } + + @NotNull + public Document getDocument() { + return myDocument; + } + + @NotNull + public String getLocalContent() { + return myLocalContent; + } + + @NotNull + public String getPatchedContent() { + return myPatchedContent; + } + + @Nullable + @Override + public String getTitle() { + return myWindowTitle; + } + + @NotNull + public String getLocalTitle() { + return myLocalTitle; + } + + @NotNull + public String getPatchedTitle() { + return myPatchedTitle; + } + + @Override + public void applyResult(@NotNull MergeResult result) { + final CharSequence applyContent; + switch (result) { + case CANCEL: + applyContent = myOriginalContent; + break; + case LEFT: + applyContent = myLocalContent; + break; + case RIGHT: + applyContent = myPatchedContent; + break; + case RESOLVED: + applyContent = null; + break; + default: + throw new IllegalArgumentException(result.name()); + } + + if (applyContent != null) { + new WriteCommandAction.Simple(myProject) { + @Override + protected void run() throws Throwable { + myDocument.setText(applyContent); + } + }.execute(); + } + + if (myCallback != null) myCallback.consume(result); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchMergeTool.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchMergeTool.java new file mode 100644 index 000000000000..5458ebafd5ed --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchMergeTool.java @@ -0,0 +1,152 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.changes.patch; + +import com.intellij.diff.DiffContentFactory; +import com.intellij.diff.FrameDiffTool; +import com.intellij.diff.contents.DocumentContent; +import com.intellij.diff.merge.*; +import com.intellij.diff.requests.SimpleDiffRequest; +import com.intellij.diff.tools.simple.SimpleDiffViewer; +import com.intellij.diff.util.DiffUtil; +import com.intellij.openapi.command.undo.DocumentReference; +import com.intellij.openapi.command.undo.DocumentReferenceManager; +import com.intellij.openapi.command.undo.UndoManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.BooleanGetter; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.EditorNotificationPanel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class ApplyPatchMergeTool implements MergeTool { + @NotNull + @Override + public MergeViewer createComponent(@NotNull MergeContext context, @NotNull MergeRequest request) { + return new MyViewer(context, (ApplyPatchMergeRequest)request); + } + + @Override + public boolean canShow(@NotNull MergeContext context, @NotNull MergeRequest request) { + return request instanceof ApplyPatchMergeRequest; + } + + private static class MyViewer implements MergeViewer { + @NotNull private final MergeContext myMergeContext; + @NotNull private final ApplyPatchMergeRequest myMergeRequest; + + @NotNull private final SimpleDiffViewer myViewer; + + public MyViewer(@NotNull MergeContext context, @NotNull ApplyPatchMergeRequest request) { + myMergeContext = context; + myMergeRequest = request; + + MergeUtil.ProxyDiffContext diffContext = new MergeUtil.ProxyDiffContext(myMergeContext); + + VirtualFile file = FileDocumentManager.getInstance().getFile(myMergeRequest.getDocument()); + final DiffContentFactory contentFactory = DiffContentFactory.getInstance(); + final DocumentContent localContent = contentFactory.create(myMergeRequest.getLocalContent(), file); + final DocumentContent mergedContent = contentFactory.create(myMergeRequest.getProject(), myMergeRequest.getDocument()); + + SimpleDiffRequest diffRequest = new SimpleDiffRequest(myMergeRequest.getTitle(), localContent, mergedContent, + myMergeRequest.getLocalTitle(), myMergeRequest.getPatchedTitle()); + DiffUtil.addNotification(new DiffIsApproximateNotification(), diffRequest); + + myViewer = new SimpleDiffViewer(diffContext, diffRequest); + } + + @NotNull + @Override + public JComponent getComponent() { + return myViewer.getComponent(); + } + + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myViewer.getPreferredFocusedComponent(); + } + + @Override + public ToolbarComponents init() { + final Project project = myMergeContext.getProject(); + final Document document = myMergeRequest.getDocument(); + + DiffUtil.executeWriteCommand(document, project, "Init merge content", new Runnable() { + @Override + public void run() { + document.setText(myMergeRequest.getPatchedContent()); + + UndoManager undoManager = project != null ? UndoManager.getInstance(project) : UndoManager.getGlobalInstance(); + if (undoManager != null) { + DocumentReference ref = DocumentReferenceManager.getInstance().create(document); + undoManager.nonundoableActionPerformed(ref, false); + } + } + }); + + ToolbarComponents components = new ToolbarComponents(); + + FrameDiffTool.ToolbarComponents init = myViewer.init(); + components.statusPanel = init.statusPanel; + components.toolbarActions = init.toolbarActions; + + components.closeHandler = new BooleanGetter() { + @Override + public boolean get() { + return MergeUtil.showExitWithoutApplyingChangesDialog(getComponent(), myMergeRequest, myMergeContext); + } + }; + return components; + } + + @Nullable + @Override + public Action getResolveAction(@NotNull final MergeResult result) { + if (result == MergeResult.LEFT || result == MergeResult.RIGHT) return null; + + String caption = MergeUtil.getResolveActionTitle(result, myMergeRequest, myMergeContext); + return new AbstractAction(caption) { + @Override + public void actionPerformed(ActionEvent e) { + if (result == MergeResult.CANCEL && + !MergeUtil.showExitWithoutApplyingChangesDialog(getComponent(), myMergeRequest, myMergeContext)) { + return; + } + myMergeContext.finishMerge(result); + } + }; + } + + @Override + public void dispose() { + Disposer.dispose(myViewer); + } + } + + public static class DiffIsApproximateNotification extends EditorNotificationPanel { + public DiffIsApproximateNotification() { + myLabel.setText("Couldn't find context for patch. Some fragments were applied at the best possible place. " + + "Please check carefully."); + } + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/BinaryFilePatchInProgress.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/BinaryFilePatchInProgress.java index 639bb0af649c..3537525a9e08 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/BinaryFilePatchInProgress.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/BinaryFilePatchInProgress.java @@ -69,7 +69,7 @@ public class BinaryFilePatchInProgress extends AbstractFilePatchInProgress textsGetter, - @NotNull String name, - @NotNull UserDataHolder context, - @NotNull ProgressIndicator indicator) + public static DiffRequest createConflictDiffRequest(@Nullable Project project, + @Nullable VirtualFile file, + @NotNull String afterTitle, + @NotNull final Getter textsGetter, + @NotNull String name, + @NotNull UserDataHolder context, + @NotNull ProgressIndicator indicator) throws DiffRequestProducerException { if (file == null) throw new DiffRequestProducerException("Can't show diff for '" + name + "'"); if (file.getFileType().isBinary()) throw new DiffRequestProducerException("Can't show diff for binary file '" + name + "'"); @@ -68,24 +78,173 @@ public class PatchDiffRequestFactory { }, indicator.getModalityState()); ApplyPatchForBaseRevisionTexts texts = textsRef.get(); + if (texts.getLocal() == null) throw new DiffRequestProducerException("Can't show diff for '" + file.getPresentableUrl() + "'"); + if (texts.getBase() == null) { - return ApplyPatchAction.createBadDiffRequest(project, file, texts); + String localContent = texts.getLocal().toString(); + String patchedContent = texts.getPatched(); + + return createBadDiffRequest(project, file, localContent, patchedContent, null, null, null); } else { - String path = FileUtil.toSystemDependentName(file.getPresentableUrl()); - FileType type = file.getFileType(); + String localContent = texts.getLocal().toString(); + String baseContent = texts.getBase().toString(); + String patchedContent = texts.getPatched(); - String windowTitle = VcsBundle.message("patch.apply.conflict.title", path); - - DiffContentFactory contentFactory = DiffContentFactory.getInstance(); - DocumentContent localContent = contentFactory.createDocument(project, file); - if (localContent == null) localContent = contentFactory.create(texts.getLocal().toString(), type); - DocumentContent baseContent = contentFactory.create(texts.getBase().toString(), type); - DocumentContent patchedContent = contentFactory.create(texts.getPatched(), type); - - return new SimpleDiffRequest(windowTitle, localContent, baseContent, patchedContent, - "Current Version", "Base Version", afterTitle); + return createDiffRequest(project, file, ContainerUtil.list(localContent, baseContent, patchedContent), null, + ContainerUtil.list("Current Version", "Base Version", afterTitle)); } } + + @NotNull + public static DiffRequest createDiffRequest(@Nullable Project project, + @Nullable VirtualFile file, + @NotNull List contents, + @Nullable String windowTitle, + @NotNull List titles) { + assert contents.size() == 3; + assert titles.size() == 3; + + if (windowTitle == null) windowTitle = getPatchTitle(file); + + String localTitle = StringUtil.notNullize(titles.get(0), VcsBundle.message("patch.apply.conflict.local.version")); + String baseTitle = StringUtil.notNullize(titles.get(1), "Base Version"); + String patchedTitle = StringUtil.notNullize(titles.get(2), VcsBundle.message("patch.apply.conflict.patched.version")); + + FileType fileType = file != null ? file.getFileType() : null; + + DiffContentFactory contentFactory = DiffContentFactory.getInstance(); + DocumentContent localContent = file != null ? contentFactory.createDocument(project, file) : null; + if (localContent == null) localContent = contentFactory.create(contents.get(0), fileType); + DocumentContent baseContent = contentFactory.create(contents.get(1), fileType); + DocumentContent patchedContent = contentFactory.create(contents.get(2), fileType); + + return new SimpleDiffRequest(windowTitle, localContent, baseContent, patchedContent, + localTitle, baseTitle, patchedTitle); + } + + @NotNull + public static DiffRequest createBadDiffRequest(@Nullable Project project, + @Nullable VirtualFile file, + @NotNull String localContent, + @NotNull String patchedContent, + @Nullable String windowTitle, + @Nullable String localTitle, + @Nullable String patchedTitle) { + if (windowTitle == null) windowTitle = getBadPatchTitle(file); + if (localTitle == null) localTitle = VcsBundle.message("patch.apply.conflict.local.version"); + if (patchedTitle == null) patchedTitle = "Patched (with problems)"; + + FileType fileType = file != null ? file.getFileType() : null; + + final DiffContentFactory contentFactory = DiffContentFactory.getInstance(); + DocumentContent leftContent = file != null ? contentFactory.createDocument(project, file) : null; + if (leftContent == null) leftContent = contentFactory.create(localContent, fileType); + DocumentContent rightContent = contentFactory.create(patchedContent, fileType); + + final DiffRequest request = new SimpleDiffRequest(windowTitle, leftContent, rightContent, localTitle, patchedTitle); + DiffUtil.addNotification(new ApplyPatchMergeTool.DiffIsApproximateNotification(), request); + + return request; + } + + @NotNull + public static MergeRequest createMergeRequest(@Nullable Project project, + @NotNull Document document, + @NotNull VirtualFile file, + @NotNull String baseContent, + @NotNull String localContent, + @NotNull String patchedContent, + @Nullable Consumer callback) + throws InvalidDiffRequestException { + List titles = ContainerUtil.list(null, null, null); + List contents = ContainerUtil.list(localContent, baseContent, patchedContent); + + return createMergeRequest(project, document, file, contents, null, titles, callback); + } + + @NotNull + public static MergeRequest createBadMergeRequest(@Nullable Project project, + @NotNull Document document, + @NotNull VirtualFile file, + @NotNull String localContent, + @NotNull String patchedContent, + @Nullable Consumer callback) + throws InvalidDiffRequestException { + return createBadMergeRequest(project, document, file, localContent, patchedContent, null, null, null, callback); + } + + @NotNull + public static MergeRequest createMergeRequest(@Nullable Project project, + @NotNull Document document, + @Nullable VirtualFile file, + @NotNull List contents, + @Nullable String windowTitle, + @NotNull List titles, + @Nullable Consumer callback) + throws InvalidDiffRequestException { + assert contents.size() == 3; + assert titles.size() == 3; + + if (windowTitle == null) windowTitle = getPatchTitle(file); + + String localTitle = StringUtil.notNullize(titles.get(0), VcsBundle.message("patch.apply.conflict.local.version")); + String baseTitle = StringUtil.notNullize(titles.get(1), VcsBundle.message("patch.apply.conflict.merged.version")); + String patchedTitle = StringUtil.notNullize(titles.get(2), VcsBundle.message("patch.apply.conflict.patched.version")); + + List actualTitles = ContainerUtil.list(localTitle, baseTitle, patchedTitle); + + FileType fileType = file != null ? file.getFileType() : null; + return DiffRequestFactory.getInstance().createMergeRequest(project, fileType, document, contents, windowTitle, actualTitles, callback); + } + + @NotNull + public static MergeRequest createBadMergeRequest(@Nullable Project project, + @NotNull Document document, + @Nullable VirtualFile file, + @NotNull String localContent, + @NotNull String patchedContent, + @Nullable String windowTitle, + @Nullable String localTitle, + @Nullable String patchedTitle, + @Nullable Consumer callback) + throws InvalidDiffRequestException { + if (!DiffUtil.canMakeWritable(document)) { + throw new InvalidDiffRequestException("Output is read only" + (file != null ? " : '" + file.getPresentableUrl() +"'": "")); + } + + if (windowTitle == null) windowTitle = getBadPatchTitle(file); + if (localTitle == null) localTitle = VcsBundle.message("patch.apply.conflict.local.version"); + if (patchedTitle == null) patchedTitle = "Patched (with problems)"; + + return new ApplyPatchMergeRequest(project, document, localContent, patchedContent, windowTitle, localTitle, patchedTitle, callback); + } + + @NotNull + private static String getPatchTitle(@Nullable VirtualFile file) { + if (file != null) { + return VcsBundle.message("patch.apply.conflict.title", getPresentablePath(file)); + } + else { + return "Patch Conflict"; + } + } + + + @NotNull + private static String getBadPatchTitle(@Nullable VirtualFile file) { + if (file != null) { + return "Result Of Patch Apply To " + getPresentablePath(file); + } + else { + return "Result Of Patch Apply"; + } + } + + @NotNull + private static String getPresentablePath(@NotNull VirtualFile file) { + String fullPath = file.getParent() == null ? file.getPath() : file.getParent().getPath(); + return file.getName() + " (" + fullPath + ")"; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/TextFilePatchInProgress.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/TextFilePatchInProgress.java index f508220c18b8..c080f456171e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/TextFilePatchInProgress.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/TextFilePatchInProgress.java @@ -105,10 +105,10 @@ public class TextFilePatchInProgress extends AbstractFilePatchInProgress parent directory, !isNewFile -> file try { - boolean isNewFile = FileStatus.ADDED.equals(shelvedChange.getFileStatus()); file = ApplyFilePatchBase.findPatchTarget(patchContext, beforePath, afterPath, isNewFile); if (!isNewFile && (file == null || !file.exists())) throw new FileNotFoundException(beforePath); } @@ -194,7 +194,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { @Override public DiffRequest process(@NotNull UserDataHolder context, @NotNull ProgressIndicator indicator) throws DiffRequestProducerException, ProcessCanceledException { - if (file != null && file.getFileType() == UnknownFileType.INSTANCE) { + if (!isNewFile && file.getFileType() == UnknownFileType.INSTANCE) { return new UnknownFileTypeDiffRequest(file, getName()); } @@ -221,7 +221,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { } }; - return PatchDiffRequestFactory.createConflict(project, file, "Shelved Version", getter, getName(), context, indicator); + return PatchDiffRequestFactory.createConflictDiffRequest(project, file, "Shelved Version", getter, getName(), context, indicator); } catch (VcsException e) { throw new DiffRequestProducerException("Can't show diff for '" + getName() + "'", e); @@ -229,7 +229,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { } else { final Change change = shelvedChange.getChange(project); - return PatchDiffRequestFactory.createFromChange(project, change, getName(), context, indicator); + return PatchDiffRequestFactory.createDiffRequest(project, change, getName(), context, indicator); } } }); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/merge/MultipleFileMergeDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/merge/MultipleFileMergeDialog.java index a73108224977..0739c539e558 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/merge/MultipleFileMergeDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/merge/MultipleFileMergeDialog.java @@ -218,6 +218,10 @@ public class MultipleFileMergeDialog extends DialogWrapper { super.dispose(); } + protected boolean beforeResolve(Collection files) { + return true; + } + @Override @NonNls protected String getDimensionServiceKey() { @@ -227,6 +231,10 @@ public class MultipleFileMergeDialog extends DialogWrapper { private void acceptRevision(final boolean isCurrent) { FileDocumentManager.getInstance().saveAllDocuments(); final Collection files = myTable.getSelection(); + if (!beforeResolve(files)) { + return; + } + for (final VirtualFile file : files) { final Ref ex = new Ref(); ApplicationManager.getApplication().runWriteAction(new Runnable() { @@ -297,7 +305,12 @@ public class MultipleFileMergeDialog extends DialogWrapper { private void showMergeDialog() { DiffRequestFactory requestFactory = DiffRequestFactory.getInstance(); - for (final VirtualFile file : myTable.getSelection()) { + Collection files = myTable.getSelection(); + if (!beforeResolve(files)) { + return; + } + + for (final VirtualFile file : files) { final MergeData mergeData; try { mergeData = myProvider.loadRevisions(file); diff --git a/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/EdgePrintElement.java b/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/EdgePrintElement.java index 99267898368c..8c20356fb754 100644 --- a/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/EdgePrintElement.java +++ b/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/EdgePrintElement.java @@ -17,8 +17,6 @@ package com.intellij.vcs.log.graph; import org.jetbrains.annotations.NotNull; -/** - */ public interface EdgePrintElement extends PrintElement { int getPositionInOtherRow(); @@ -29,6 +27,8 @@ public interface EdgePrintElement extends PrintElement { @NotNull LineStyle getLineStyle(); + boolean hasArrow(); + enum Type { UP, DOWN diff --git a/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/SimplePrintElement.java b/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/NodePrintElement.java similarity index 76% rename from platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/SimplePrintElement.java rename to platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/NodePrintElement.java index bdcd529bc57b..db1b9e39384a 100644 --- a/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/SimplePrintElement.java +++ b/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/NodePrintElement.java @@ -15,19 +15,5 @@ */ package com.intellij.vcs.log.graph; -import org.jetbrains.annotations.NotNull; - -/** - */ -public interface SimplePrintElement extends PrintElement { - - @NotNull - Type getType(); - - enum Type { - NODE, - UP_ARROW, - DOWN_ARROW - } - +public interface NodePrintElement extends PrintElement { } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/VisibleGraphImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/VisibleGraphImpl.java index c230e0fc0fa6..a1c40e759dfe 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/VisibleGraphImpl.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/VisibleGraphImpl.java @@ -35,8 +35,6 @@ import java.awt.*; import java.util.Collection; import java.util.Collections; -import static com.intellij.vcs.log.graph.SimplePrintElement.Type.DOWN_ARROW; -import static com.intellij.vcs.log.graph.SimplePrintElement.Type.UP_ARROW; import static com.intellij.vcs.log.graph.utils.LinearGraphUtils.getCursor; public class VisibleGraphImpl implements VisibleGraph { @@ -128,9 +126,9 @@ public class VisibleGraphImpl implements VisibleGraph { @Nullable private GraphAnswer performArrowAction(@NotNull LinearGraphAction action) { PrintElementWithGraphElement affectedElement = action.getAffectedElement(); - if (!(affectedElement instanceof SimplePrintElement)) return null; - SimplePrintElement.Type printElementType = ((SimplePrintElement)affectedElement).getType(); - if (printElementType != DOWN_ARROW && printElementType != UP_ARROW) return null; + if (!(affectedElement instanceof EdgePrintElement)) return null; + EdgePrintElement edgePrintElement = (EdgePrintElement)affectedElement; + if (!edgePrintElement.hasArrow()) return null; GraphElement graphElement = affectedElement.getGraphElement(); if (!(graphElement instanceof GraphEdge)) return null; @@ -138,11 +136,11 @@ public class VisibleGraphImpl implements VisibleGraph { Integer targetId = null; if (edge.getType() == GraphEdgeType.NOT_LOAD_COMMIT) { - assert printElementType == DOWN_ARROW; + assert edgePrintElement.getType().equals(EdgePrintElement.Type.DOWN); targetId = edge.getTargetId(); } if (edge.getType().isNormalEdge()) { - if (printElementType == DOWN_ARROW) { + if (edgePrintElement.getType().equals(EdgePrintElement.Type.DOWN)) { targetId = convertToNodeId(edge.getDownNodeIndex()); } else { diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/AbstractPrintElementGenerator.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/AbstractPrintElementGenerator.java index 3906f8831766..8bf88dade069 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/AbstractPrintElementGenerator.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/AbstractPrintElementGenerator.java @@ -15,9 +15,9 @@ */ package com.intellij.vcs.log.graph.impl.print; +import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.graph.EdgePrintElement; import com.intellij.vcs.log.graph.PrintElement; -import com.intellij.vcs.log.graph.SimplePrintElement; import com.intellij.vcs.log.graph.api.LinearGraph; import com.intellij.vcs.log.graph.api.elements.GraphEdge; import com.intellij.vcs.log.graph.api.elements.GraphElement; @@ -30,6 +30,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; +import java.util.Map; public abstract class AbstractPrintElementGenerator implements PrintElementGenerator { @@ -45,30 +46,59 @@ public abstract class AbstractPrintElementGenerator implements PrintElementGener public Collection getPrintElements(int rowIndex) { Collection result = new ArrayList(); + Map arrows = ContainerUtil.newHashMap(); + + for (SimpleRowElement rowElement : getSimpleRowElements(rowIndex)) { + if (!rowElement.myType.equals(RowElementType.NODE)) { + arrows.put((GraphEdge)rowElement.myElement, rowElement); + } + } + if (rowIndex < myLinearGraph.nodesCount() - 1) { for (ShortEdge shortEdge : getDownShortEdges(rowIndex)) { - result.add(createEdgePrintElement(rowIndex, shortEdge, EdgePrintElement.Type.DOWN)); + RowElementType rowElementType = RowElementType.NODE; + if ((arrows.get(shortEdge.myEdge) != null) && RowElementType.DOWN_ARROW.equals(arrows.get(shortEdge.myEdge).myType)) { + rowElementType = RowElementType.DOWN_ARROW; + arrows.remove(shortEdge.myEdge); + } + result.add(createEdgePrintElement(rowIndex, shortEdge, EdgePrintElement.Type.DOWN, !rowElementType.equals(RowElementType.NODE))); } } if (rowIndex > 0) { for (ShortEdge shortEdge : getDownShortEdges(rowIndex - 1)) { - result.add(createEdgePrintElement(rowIndex, shortEdge, EdgePrintElement.Type.UP)); + RowElementType rowElementType = RowElementType.NODE; + if ((arrows.get(shortEdge.myEdge) != null) && RowElementType.UP_ARROW.equals(arrows.get(shortEdge.myEdge).myType)) { + rowElementType = RowElementType.UP_ARROW; + arrows.remove(shortEdge.myEdge); + } + result.add(createEdgePrintElement(rowIndex, shortEdge, EdgePrintElement.Type.UP, !rowElementType.equals(RowElementType.NODE))); } } + for (SimpleRowElement arrow : arrows.values()) { + result.add(new EdgePrintElementImpl(rowIndex, arrow.myPosition, arrow.myPosition, + arrow.myType == RowElementType.UP_ARROW ? EdgePrintElement.Type.UP : EdgePrintElement.Type.DOWN, + (GraphEdge)arrow.myElement, true, myPrintElementManager)); + } + for (SimpleRowElement rowElement : getSimpleRowElements(rowIndex)) { - result.add(createSimplePrintElement(rowIndex, rowElement)); + if (rowElement.myType.equals(RowElementType.NODE)) { + result.add(createSimplePrintElement(rowIndex, rowElement)); + } } return result; } private SimplePrintElementImpl createSimplePrintElement(int rowIndex, SimpleRowElement rowElement) { - return new SimplePrintElementImpl(rowIndex, rowElement.myPosition, rowElement.myType, rowElement.myElement, myPrintElementManager); + return new SimplePrintElementImpl(rowIndex, rowElement.myPosition, rowElement.myElement, myPrintElementManager); } - private EdgePrintElementImpl createEdgePrintElement(int rowIndex, @NotNull ShortEdge shortEdge, @NotNull EdgePrintElement.Type type) { + private EdgePrintElementImpl createEdgePrintElement(int rowIndex, + @NotNull ShortEdge shortEdge, + @NotNull EdgePrintElement.Type type, + @NotNull boolean hasArrow) { int positionInCurrentRow, positionInOtherRow; if (type == EdgePrintElement.Type.DOWN) { positionInCurrentRow = shortEdge.myUpPosition; @@ -78,7 +108,8 @@ public abstract class AbstractPrintElementGenerator implements PrintElementGener positionInCurrentRow = shortEdge.myDownPosition; positionInOtherRow = shortEdge.myUpPosition; } - return new EdgePrintElementImpl(rowIndex, positionInCurrentRow, positionInOtherRow, type, shortEdge.myEdge, myPrintElementManager); + return new EdgePrintElementImpl(rowIndex, positionInCurrentRow, positionInOtherRow, type, shortEdge.myEdge, hasArrow, + myPrintElementManager); } @NotNull @@ -116,13 +147,19 @@ public abstract class AbstractPrintElementGenerator implements PrintElementGener protected static class SimpleRowElement { @NotNull public final GraphElement myElement; - @NotNull public final SimplePrintElement.Type myType; + @NotNull public final RowElementType myType; public final int myPosition; - public SimpleRowElement(@NotNull GraphElement element, @NotNull SimplePrintElement.Type type, int position) { + public SimpleRowElement(@NotNull GraphElement element, @NotNull RowElementType type, int position) { myElement = element; myPosition = position; myType = type; } } + + enum RowElementType { + NODE, + UP_ARROW, + DOWN_ARROW + } } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/PrintElementGeneratorImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/PrintElementGeneratorImpl.java index c254c220d69d..49b25e3ccf46 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/PrintElementGeneratorImpl.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/PrintElementGeneratorImpl.java @@ -20,7 +20,6 @@ import com.intellij.openapi.util.Pair; import com.intellij.util.NullableFunction; import com.intellij.util.SmartList; import com.intellij.util.containers.SLRUMap; -import com.intellij.vcs.log.graph.SimplePrintElement; import com.intellij.vcs.log.graph.api.EdgeFilter; import com.intellij.vcs.log.graph.api.LinearGraph; import com.intellij.vcs.log.graph.api.elements.GraphEdge; @@ -151,7 +150,7 @@ public class PrintElementGeneratorImpl extends AbstractPrintElementGenerator { for (int position = 0; position < sortedVisibleElementsInRow.size(); position++) { GraphElement element = sortedVisibleElementsInRow.get(position); if (element instanceof GraphNode) { - result.add(new SimpleRowElement(element, SimplePrintElement.Type.NODE, position)); + result.add(new SimpleRowElement(element, RowElementType.NODE, position)); } if (element instanceof GraphEdge) { @@ -172,13 +171,13 @@ public class PrintElementGeneratorImpl extends AbstractPrintElementGenerator { case DOTTED_ARROW_DOWN: case NOT_LOAD_COMMIT: if (intEqual(edge.getUpNodeIndex(), visibleRowIndex - 1)) { - result.add(new SimpleRowElement(edge, SimplePrintElement.Type.DOWN_ARROW, position)); + result.add(new SimpleRowElement(edge, RowElementType.DOWN_ARROW, position)); } break; case DOTTED_ARROW_UP: if (intEqual(edge.getDownNodeIndex(), visibleRowIndex + 1)) // todo case 0-row arrow { - result.add(new SimpleRowElement(edge, SimplePrintElement.Type.UP_ARROW, position)); + result.add(new SimpleRowElement(edge, RowElementType.UP_ARROW, position)); } break; default: @@ -196,9 +195,9 @@ public class PrintElementGeneratorImpl extends AbstractPrintElementGenerator { int upOffset, int downOffset, int showingPartSize) { - if (upOffset == showingPartSize) result.add(new SimpleRowElement(edge, SimplePrintElement.Type.DOWN_ARROW, position)); + if (upOffset == showingPartSize) result.add(new SimpleRowElement(edge, RowElementType.DOWN_ARROW, position)); - if (downOffset == showingPartSize) result.add(new SimpleRowElement(edge, SimplePrintElement.Type.UP_ARROW, position)); + if (downOffset == showingPartSize) result.add(new SimpleRowElement(edge, RowElementType.UP_ARROW, position)); } private boolean edgeIsVisibleInRow(@NotNull GraphEdge edge, int visibleRowIndex) { diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/elements/EdgePrintElementImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/elements/EdgePrintElementImpl.java index d8e95beec9cb..84e33a588ee2 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/elements/EdgePrintElementImpl.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/elements/EdgePrintElementImpl.java @@ -41,17 +41,20 @@ public class EdgePrintElementImpl extends PrintElementWithGraphElement implement @NotNull private final Type myType; @NotNull private final LineStyle myLineStyle; private final int myPositionInOtherRow; + private final boolean myHasArrow; public EdgePrintElementImpl(int rowIndex, int positionInCurrentRow, int positionInOtherRow, @NotNull Type type, @NotNull GraphEdge graphEdge, + @NotNull boolean hasArrow, @NotNull PrintElementManager printElementManager) { super(rowIndex, positionInCurrentRow, graphEdge, printElementManager); myType = type; myLineStyle = convertToLineStyle(graphEdge.getType()); myPositionInOtherRow = positionInOtherRow; + myHasArrow = hasArrow; } @Override @@ -71,6 +74,11 @@ public class EdgePrintElementImpl extends PrintElementWithGraphElement implement return myLineStyle; } + @Override + public boolean hasArrow() { + return myHasArrow; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -82,6 +90,7 @@ public class EdgePrintElementImpl extends PrintElementWithGraphElement implement if (myPositionInOtherRow != that.getPositionInOtherRow()) return false; if (myRowIndex != that.getRowIndex()) return false; if (myType != that.getType()) return false; + if (myHasArrow != that.hasArrow()) return false; return true; } @@ -92,6 +101,7 @@ public class EdgePrintElementImpl extends PrintElementWithGraphElement implement result = 31 * result + myPositionInCurrentRow; result = 31 * result + myPositionInOtherRow; result = 37 * result + myType.hashCode(); + result = 31 * result + (myHasArrow ? 1 : 0); return result; } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/elements/SimplePrintElementImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/elements/SimplePrintElementImpl.java index 3dd25d75bc74..ed28cf196f08 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/elements/SimplePrintElementImpl.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/print/elements/SimplePrintElementImpl.java @@ -16,40 +16,29 @@ package com.intellij.vcs.log.graph.impl.print.elements; -import com.intellij.vcs.log.graph.SimplePrintElement; +import com.intellij.vcs.log.graph.NodePrintElement; import com.intellij.vcs.log.graph.api.elements.GraphElement; import com.intellij.vcs.log.graph.api.printer.PrintElementManager; import org.jetbrains.annotations.NotNull; -public class SimplePrintElementImpl extends PrintElementWithGraphElement implements SimplePrintElement { - - @NotNull private final Type myType; +public class SimplePrintElementImpl extends PrintElementWithGraphElement implements NodePrintElement { public SimplePrintElementImpl(int rowIndex, int positionInCurrentRow, - @NotNull Type type, @NotNull GraphElement graphElement, @NotNull PrintElementManager printElementManager) { super(rowIndex, positionInCurrentRow, graphElement, printElementManager); - myType = type; - } - - @NotNull - @Override - public Type getType() { - return myType; } @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof SimplePrintElement)) return false; + if (!(o instanceof NodePrintElement)) return false; - SimplePrintElement that = (SimplePrintElement)o; + NodePrintElement that = (NodePrintElement)o; if (myPositionInCurrentRow != that.getPositionInCurrentRow()) return false; if (myRowIndex != that.getRowIndex()) return false; - if (myType != that.getType()) return false; return true; } @@ -58,7 +47,6 @@ public class SimplePrintElementImpl extends PrintElementWithGraphElement impleme public int hashCode() { int result = myRowIndex; result = 31 * result + myPositionInCurrentRow; - result = 37 * result + myType.hashCode(); return result; } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/PositionUtil.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/PositionUtil.java index ea5d08ccc363..ce5e07c453a6 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/PositionUtil.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/PositionUtil.java @@ -15,7 +15,6 @@ */ package com.intellij.vcs.log.printer.idea; -import com.intellij.vcs.log.graph.SimplePrintElement; import org.jetbrains.annotations.NotNull; import java.awt.*; @@ -46,12 +45,10 @@ public class PositionUtil { return distance(x1, y1, x, y) + distance(x2, y2, x, y) < distance(x1, y1, x2, y2) + thick; } - public static boolean overNode(int position, int x, int y, SimplePrintElement.Type type, int rowHeight) { + public static boolean overNode(int position, int x, int y, int rowHeight) { int r = CIRCLE_RADIUS; int x0 = WIDTH_NODE * position + WIDTH_NODE / 2; int y0 = rowHeight / 2; - if (type == SimplePrintElement.Type.DOWN_ARROW) y0 = rowHeight - r; - if (type == SimplePrintElement.Type.UP_ARROW) y0 = r; return distance(x0, y0, x, y) <= r; } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/PrintParameters.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/PrintParameters.java index ae1849186f43..b2a066130fdf 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/PrintParameters.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/PrintParameters.java @@ -1,8 +1,5 @@ package com.intellij.vcs.log.printer.idea; -/** - * @author erokhins - */ public class PrintParameters { public static final int WIDTH_NODE = 15; @@ -11,4 +8,5 @@ public class PrintParameters { public static final float THICK_LINE = 1.5f; public static final float SELECT_THICK_LINE = 2.5f; + public static final int LABEL_PADDING = 3; } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/SimpleGraphCellPainter.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/SimpleGraphCellPainter.java index a8175906322f..90a35c3991ee 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/SimpleGraphCellPainter.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/printer/idea/SimpleGraphCellPainter.java @@ -15,15 +15,17 @@ */ package com.intellij.vcs.log.printer.idea; +import com.intellij.openapi.util.Pair; import com.intellij.ui.JBColor; import com.intellij.vcs.log.graph.EdgePrintElement; +import com.intellij.vcs.log.graph.NodePrintElement; import com.intellij.vcs.log.graph.PrintElement; -import com.intellij.vcs.log.graph.SimplePrintElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; import java.util.Collection; /** @@ -33,6 +35,8 @@ public class SimpleGraphCellPainter implements GraphCellPainter { private static final Color MARK_COLOR = JBColor.BLACK; private static final int ROW_HEIGHT = 24; + private static final double ARROW_ANGLE_COS2 = 0.7; + private static final double ARROW_LENGTH = 0.3; private final Stroke usual = new BasicStroke(PrintParameters.THICK_LINE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL); private final Stroke hide = @@ -53,22 +57,66 @@ public class SimpleGraphCellPainter implements GraphCellPainter { return ROW_HEIGHT; } - private void paintUpLine(int from, int to, Color color) { - int x1 = PrintParameters.WIDTH_NODE * from + PrintParameters.WIDTH_NODE / 2; - int y1 = getRowHeight() / 2; - int x2 = PrintParameters.WIDTH_NODE * to + PrintParameters.WIDTH_NODE / 2; - int y2 = -getRowHeight() / 2; - g2.setColor(color); - g2.drawLine(x2, y2, x1, y1); + private void paintUpLine(int from, int to, Color color, boolean hasArrow) { + // paint vertical lines normal size + // paint non-vertical lines twice the size to make them dock with each other well + if (from == to) { + int x = PrintParameters.WIDTH_NODE * from + PrintParameters.WIDTH_NODE / 2; + int y1 = getRowHeight() / 2 - 1; + int y2 = 0; + paintLine(color, hasArrow, x, y1, x, y2, x, y2); + } + else { + int x1 = PrintParameters.WIDTH_NODE * from + PrintParameters.WIDTH_NODE / 2; + int y1 = getRowHeight() / 2; + int x2 = PrintParameters.WIDTH_NODE * to + PrintParameters.WIDTH_NODE / 2; + int y2 = -getRowHeight() / 2; + paintLine(color, hasArrow, x1, y1, x2, y2, (x1 + x2) / 2, (y1 + y2) / 2); + } } - private void paintDownLine(int from, int to, Color color) { - int x1 = PrintParameters.WIDTH_NODE * from + PrintParameters.WIDTH_NODE / 2; - int y1 = getRowHeight() / 2; - int x2 = PrintParameters.WIDTH_NODE * to + PrintParameters.WIDTH_NODE / 2; - int y2 = getRowHeight() + getRowHeight() / 2; + private void paintDownLine(int from, int to, Color color, boolean hasArrow) { + if (from == to) { + int y2 = getRowHeight() - 1; + int y1 = getRowHeight() / 2; + int x = PrintParameters.WIDTH_NODE * from + PrintParameters.WIDTH_NODE / 2; + paintLine(color, hasArrow, x, y1, x, y2, x, y2); + } + else { + int x1 = PrintParameters.WIDTH_NODE * from + PrintParameters.WIDTH_NODE / 2; + int y1 = getRowHeight() / 2; + int x2 = PrintParameters.WIDTH_NODE * to + PrintParameters.WIDTH_NODE / 2; + int y2 = getRowHeight() + getRowHeight() / 2; + paintLine(color, hasArrow, x1, y1, x2, y2, (x1 + x2) / 2, (y1 + y2) / 2); + } + } + + private void paintLine(Color color, boolean hasArrow, int x1, int y1, int x2, int y2, int startArrowX, int startArrowY) { g2.setColor(color); g2.drawLine(x1, y1, x2, y2); + if (hasArrow) { + Pair rotate1 = + rotate(x1, y1, startArrowX, startArrowY, Math.sqrt(ARROW_ANGLE_COS2), Math.sqrt(1 - ARROW_ANGLE_COS2), ARROW_LENGTH * getRowHeight()); + Pair rotate2 = + rotate(x1, y1, startArrowX, startArrowY, Math.sqrt(ARROW_ANGLE_COS2), -Math.sqrt(1 - ARROW_ANGLE_COS2), ARROW_LENGTH * getRowHeight()); + g2.drawLine(startArrowX, startArrowY, rotate1.first, rotate1.second); + g2.drawLine(startArrowX, startArrowY, rotate2.first, rotate2.second); + } + } + + @NotNull + private static Pair rotate(double x, double y, double centerX, double centerY, double cos, double sin, double arrowLength) { + double translateX = (x - centerX); + double translateY = (y - centerY); + + double d = Math.sqrt(translateX * translateX + translateY * translateY); + double scaleX = arrowLength * translateX / d; + double scaleY = arrowLength * translateY / d; + + double rotateX = scaleX * cos - scaleY * sin; + double rotateY = scaleX * sin + scaleY * cos; + + return Pair.create((int)Math.round(rotateX + centerX), (int)Math.round(rotateY + centerY)); } private void paintCircle(int position, Color color, boolean select) { @@ -83,26 +131,6 @@ public class SimpleGraphCellPainter implements GraphCellPainter { g2.fill(circle); } - private void paintDownArrow(int position, Color color) { - int x0 = PrintParameters.WIDTH_NODE * position + PrintParameters.WIDTH_NODE / 2; - int r = PrintParameters.CIRCLE_RADIUS; - int y0 = getRowHeight() - r - 2; - g2.setColor(color); - g2.drawLine(x0, getRowHeight() / 2, x0, y0 + r); - g2.drawLine(x0, y0 + r, x0 + r, y0); - g2.drawLine(x0, y0 + r, x0 - r, y0); - } - - private void paintUpArrow(int position, Color color) { - int x0 = PrintParameters.WIDTH_NODE * position + PrintParameters.WIDTH_NODE / 2; - int r = PrintParameters.CIRCLE_RADIUS; - int y0 = r + 2; - g2.setColor(color); - g2.drawLine(x0, getRowHeight() / 2, x0, y0 - r); - g2.drawLine(x0, y0 - r, x0 + r, y0); - g2.drawLine(x0, y0 - r, x0 - r, y0); - } - private void setStroke(boolean usual, boolean select) { if (usual) { if (select) { @@ -166,43 +194,23 @@ public class SimpleGraphCellPainter implements GraphCellPainter { int to = edgePrintElement.getPositionInOtherRow(); if (edgePrintElement.getType() == EdgePrintElement.Type.DOWN) { - paintDownLine(from, to, color); + paintDownLine(from, to, color, edgePrintElement.hasArrow()); } else { - paintUpLine(from, to, color); + paintUpLine(from, to, color, edgePrintElement.hasArrow()); } } }; } - if (printElement instanceof SimplePrintElement) { - final int position = printElement.getPositionInCurrentRow(); - switch (((SimplePrintElement)printElement).getType()) { - case NODE: - if (printElement.isSelected()) { - paintCircle(position, MARK_COLOR, true); - paintCircle(position, getColor(printElement), false); - } - else { - paintCircle(position, getColor(printElement), false); - } - break; - case UP_ARROW: - printer = new LitePrinter() { - @Override - public void print(Color color) { - paintUpArrow(position, color); - } - }; - break; - case DOWN_ARROW: - printer = new LitePrinter() { - @Override - public void print(Color color) { - paintDownArrow(position, color); - } - }; - break; + if (printElement instanceof NodePrintElement) { + int position = printElement.getPositionInCurrentRow(); + if (printElement.isSelected()) { + paintCircle(position, MARK_COLOR, true); + paintCircle(position, getColor(printElement), false); + } + else { + paintCircle(position, getColor(printElement), false); } } @@ -214,8 +222,8 @@ public class SimpleGraphCellPainter implements GraphCellPainter { @Override public PrintElement mouseOver(@NotNull Collection printElements, int x, int y) { for (PrintElement printElement : printElements) { - if (printElement instanceof SimplePrintElement) { - if (PositionUtil.overNode(printElement.getPositionInCurrentRow(), x, y, ((SimplePrintElement)printElement).getType(), getRowHeight())) { + if (printElement instanceof NodePrintElement) { + if (PositionUtil.overNode(printElement.getPositionInCurrentRow(), x, y, getRowHeight())) { return printElement; } } diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/StrUtils.kt b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/StrUtils.kt index f857b9f174b7..195ebecf2976 100644 --- a/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/StrUtils.kt +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/StrUtils.kt @@ -61,15 +61,15 @@ fun PrintElementWithGraphElement.asString(): String { val pos = getPositionInCurrentRow() val sel = if (isSelected()) "Select" else "Unselect" return when (this) { - is SimplePrintElement -> { - val t = getType() - "Simple:${t}|-$row:${pos}|-$color:${sel}($element)" + is NodePrintElement -> { + "Simple:NODE|-$row:${pos}|-$color:${sel}($element)" } is EdgePrintElement -> { val t = getType() val ls = getLineStyle() val posO = getPositionInOtherRow() - "Edge:$t:${ls}|-$row:$pos:${posO}|-$color:$sel($element)" + val arrow = if (hasArrow()) "_ARROW" else "" + "Edge:$t${arrow}:${ls}|-$row:$pos:${posO}|-$color:$sel($element)" } else -> { @@ -85,8 +85,8 @@ fun PrintElementGenerator.asString(size: Int): String { if (row > 0) s.append("\n") val elements = getPrintElements(row).sortBy { val pos = it.getPositionInCurrentRow() - if (it is SimplePrintElement) { - 1024 * pos + it.getType().ordinal() + if (it is NodePrintElement) { + 1024 * pos } else if (it is EdgePrintElement) { 1024 * pos + (it.getType().ordinal() + 1) * 64 + it.getPositionInOtherRow() } else 0 diff --git a/platform/vcs-log/graph/testData/elementGenerator/longEdges_out.txt b/platform/vcs-log/graph/testData/elementGenerator/longEdges_out.txt index 174e2b26843f..50f72a31f891 100644 --- a/platform/vcs-log/graph/testData/elementGenerator/longEdges_out.txt +++ b/platform/vcs-log/graph/testData/elementGenerator/longEdges_out.txt @@ -11,9 +11,8 @@ Simple:NODE|-0:0|-0:Unselect(0_U) Edge:DOWN:SOLID|-0:0:9|-9:Unselect(0:9:n_U) Edge:DOWN:SOLID|-0:0:10|-10:Unselect(0:10:n_U) Edge:DOWN:SOLID|-0:0:11|-11:Unselect(0:11:n_U) -Simple:DOWN_ARROW|-1:0|-12:Unselect(0:12:n_U) - Edge:UP:SOLID|-1:0:0|-12:Unselect(0:12:n_U) - Edge:DOWN:SOLID|-1:0:0|-12:Unselect(0:12:n_U) +Edge:UP:SOLID|-1:0:0|-12:Unselect(0:12:n_U) + Edge:DOWN_ARROW:SOLID|-1:0:0|-12:Unselect(0:12:n_U) Simple:NODE|-1:1|-1:Unselect(1_U) Edge:UP:SOLID|-1:1:0|-1:Unselect(0:1:n_U) Edge:DOWN:SOLID|-1:1:1|-13:Unselect(1:12:n_U) @@ -33,17 +32,14 @@ Simple:DOWN_ARROW|-1:0|-12:Unselect(0:12:n_U) Edge:DOWN:SOLID|-1:8:8|-8:Unselect(0:8:n_U) Edge:UP:SOLID|-1:9:0|-9:Unselect(0:9:n_U) Edge:DOWN:SOLID|-1:9:9|-9:Unselect(0:9:n_U) - Simple:DOWN_ARROW|-1:10|-10:Unselect(0:10:n_U) Edge:UP:SOLID|-1:10:0|-10:Unselect(0:10:n_U) - Edge:DOWN:SOLID|-1:10:10|-10:Unselect(0:10:n_U) - Simple:DOWN_ARROW|-1:11|-11:Unselect(0:11:n_U) + Edge:DOWN_ARROW:SOLID|-1:10:10|-10:Unselect(0:10:n_U) Edge:UP:SOLID|-1:11:0|-11:Unselect(0:11:n_U) - Edge:DOWN:SOLID|-1:11:11|-11:Unselect(0:11:n_U) -Simple:DOWN_ARROW|-2:0|-12:Unselect(0:12:n_U) - Edge:UP:SOLID|-2:0:0|-12:Unselect(0:12:n_U) - Simple:DOWN_ARROW|-2:1|-13:Unselect(1:12:n_U) + Edge:DOWN_ARROW:SOLID|-1:11:11|-11:Unselect(0:11:n_U) +Edge:UP:SOLID|-2:0:0|-12:Unselect(0:12:n_U) + Edge:DOWN_ARROW:SOLID|-2:0:0|-12:Unselect(0:12:n_U) Edge:UP:SOLID|-2:1:1|-13:Unselect(1:12:n_U) - Edge:DOWN:SOLID|-2:1:0|-13:Unselect(1:12:n_U) + Edge:DOWN_ARROW:SOLID|-2:1:0|-13:Unselect(1:12:n_U) Simple:NODE|-2:2|-2:Unselect(2_U) Edge:UP:SOLID|-2:2:2|-2:Unselect(0:2:n_U) Edge:DOWN:SOLID|-2:2:1|-14:Unselect(2:12:n_U) @@ -55,21 +51,20 @@ Simple:DOWN_ARROW|-2:0|-12:Unselect(0:12:n_U) Edge:DOWN:SOLID|-2:5:4|-5:Unselect(0:5:n_U) Edge:UP:SOLID|-2:6:6|-6:Unselect(0:6:n_U) Edge:DOWN:SOLID|-2:6:5|-6:Unselect(0:6:n_U) - Simple:DOWN_ARROW|-2:7|-7:Unselect(0:7:n_U) Edge:UP:SOLID|-2:7:7|-7:Unselect(0:7:n_U) - Simple:DOWN_ARROW|-2:8|-8:Unselect(0:8:n_U) + Edge:DOWN_ARROW:SOLID|-2:7:7|-7:Unselect(0:7:n_U) Edge:UP:SOLID|-2:8:8|-8:Unselect(0:8:n_U) - Simple:DOWN_ARROW|-2:9|-9:Unselect(0:9:n_U) + Edge:DOWN_ARROW:SOLID|-2:8:8|-8:Unselect(0:8:n_U) Edge:UP:SOLID|-2:9:9|-9:Unselect(0:9:n_U) - Simple:DOWN_ARROW|-2:10|-10:Unselect(0:10:n_U) + Edge:DOWN_ARROW:SOLID|-2:9:9|-9:Unselect(0:9:n_U) Edge:UP:SOLID|-2:10:10|-10:Unselect(0:10:n_U) - Simple:DOWN_ARROW|-2:11|-11:Unselect(0:11:n_U) + Edge:DOWN_ARROW:SOLID|-2:10:10|-10:Unselect(0:10:n_U) Edge:UP:SOLID|-2:11:11|-11:Unselect(0:11:n_U) -Simple:DOWN_ARROW|-3:0|-13:Unselect(1:12:n_U) - Edge:UP:SOLID|-3:0:1|-13:Unselect(1:12:n_U) - Simple:DOWN_ARROW|-3:1|-14:Unselect(2:12:n_U) + Edge:DOWN_ARROW:SOLID|-2:11:11|-11:Unselect(0:11:n_U) +Edge:UP:SOLID|-3:0:1|-13:Unselect(1:12:n_U) + Edge:DOWN_ARROW:SOLID|-3:0:0|-13:Unselect(1:12:n_U) Edge:UP:SOLID|-3:1:2|-14:Unselect(2:12:n_U) - Edge:DOWN:SOLID|-3:1:0|-14:Unselect(2:12:n_U) + Edge:DOWN_ARROW:SOLID|-3:1:0|-14:Unselect(2:12:n_U) Simple:NODE|-3:2|-3:Unselect(3_U) Edge:UP:SOLID|-3:2:3|-3:Unselect(0:3:n_U) Edge:DOWN:SOLID|-3:2:1|-15:Unselect(3:12:n_U) @@ -79,8 +74,8 @@ Simple:DOWN_ARROW|-3:0|-13:Unselect(1:12:n_U) Edge:DOWN:SOLID|-3:4:3|-5:Unselect(0:5:n_U) Edge:UP:SOLID|-3:5:6|-6:Unselect(0:6:n_U) Edge:DOWN:SOLID|-3:5:4|-6:Unselect(0:6:n_U) -Simple:DOWN_ARROW|-4:0|-14:Unselect(2:12:n_U) - Edge:UP:SOLID|-4:0:1|-14:Unselect(2:12:n_U) +Edge:UP:SOLID|-4:0:1|-14:Unselect(2:12:n_U) + Edge:DOWN_ARROW:SOLID|-4:0:0|-14:Unselect(2:12:n_U) Edge:UP:SOLID|-4:1:2|-15:Unselect(3:12:n_U) Edge:DOWN:SOLID|-4:1:0|-15:Unselect(3:12:n_U) Simple:NODE|-4:2|-4:Unselect(4_U) @@ -90,8 +85,8 @@ Simple:DOWN_ARROW|-4:0|-14:Unselect(2:12:n_U) Edge:DOWN:SOLID|-4:3:2|-5:Unselect(0:5:n_U) Edge:UP:SOLID|-4:4:5|-6:Unselect(0:6:n_U) Edge:DOWN:SOLID|-4:4:3|-6:Unselect(0:6:n_U) -Simple:DOWN_ARROW|-5:0|-15:Unselect(3:12:n_U) - Edge:UP:SOLID|-5:0:1|-15:Unselect(3:12:n_U) +Edge:UP:SOLID|-5:0:1|-15:Unselect(3:12:n_U) + Edge:DOWN_ARROW:SOLID|-5:0:0|-15:Unselect(3:12:n_U) Edge:UP:SOLID|-5:1:2|-16:Unselect(4:12:n_U) Edge:DOWN:SOLID|-5:1:0|-16:Unselect(4:12:n_U) Simple:NODE|-5:2|-5:Unselect(5_U) @@ -99,10 +94,10 @@ Simple:DOWN_ARROW|-5:0|-15:Unselect(3:12:n_U) Edge:DOWN:SOLID|-5:2:1|-17:Unselect(5:12:n_U) Edge:UP:SOLID|-5:3:4|-6:Unselect(0:6:n_U) Edge:DOWN:SOLID|-5:3:2|-6:Unselect(0:6:n_U) - Simple:UP_ARROW|-5:4|-7:Unselect(0:7:n_U) + Edge:UP_ARROW:SOLID|-5:4:4|-7:Unselect(0:7:n_U) Edge:DOWN:SOLID|-5:4:3|-7:Unselect(0:7:n_U) -Simple:DOWN_ARROW|-6:0|-16:Unselect(4:12:n_U) - Edge:UP:SOLID|-6:0:1|-16:Unselect(4:12:n_U) +Edge:UP:SOLID|-6:0:1|-16:Unselect(4:12:n_U) + Edge:DOWN_ARROW:SOLID|-6:0:0|-16:Unselect(4:12:n_U) Edge:UP:SOLID|-6:1:2|-17:Unselect(5:12:n_U) Edge:DOWN:SOLID|-6:1:0|-17:Unselect(5:12:n_U) Simple:NODE|-6:2|-6:Unselect(6_U) @@ -110,10 +105,10 @@ Simple:DOWN_ARROW|-6:0|-16:Unselect(4:12:n_U) Edge:DOWN:SOLID|-6:2:1|-18:Unselect(6:12:n_U) Edge:UP:SOLID|-6:3:4|-7:Unselect(0:7:n_U) Edge:DOWN:SOLID|-6:3:2|-7:Unselect(0:7:n_U) - Simple:UP_ARROW|-6:4|-8:Unselect(0:8:n_U) + Edge:UP_ARROW:SOLID|-6:4:4|-8:Unselect(0:8:n_U) Edge:DOWN:SOLID|-6:4:3|-8:Unselect(0:8:n_U) -Simple:DOWN_ARROW|-7:0|-17:Unselect(5:12:n_U) - Edge:UP:SOLID|-7:0:1|-17:Unselect(5:12:n_U) +Edge:UP:SOLID|-7:0:1|-17:Unselect(5:12:n_U) + Edge:DOWN_ARROW:SOLID|-7:0:0|-17:Unselect(5:12:n_U) Edge:UP:SOLID|-7:1:2|-18:Unselect(6:12:n_U) Edge:DOWN:SOLID|-7:1:0|-18:Unselect(6:12:n_U) Simple:NODE|-7:2|-7:Unselect(7_U) @@ -121,7 +116,7 @@ Simple:DOWN_ARROW|-7:0|-17:Unselect(5:12:n_U) Edge:DOWN:SOLID|-7:2:1|-19:Unselect(7:12:n_U) Edge:UP:SOLID|-7:3:4|-8:Unselect(0:8:n_U) Edge:DOWN:SOLID|-7:3:2|-8:Unselect(0:8:n_U) - Simple:UP_ARROW|-7:4|-9:Unselect(0:9:n_U) + Edge:UP_ARROW:SOLID|-7:4:4|-9:Unselect(0:9:n_U) Edge:DOWN:SOLID|-7:4:3|-9:Unselect(0:9:n_U) Edge:UP:SOLID|-8:0:1|-18:Unselect(6:12:n_U) Edge:DOWN:SOLID|-8:0:0|-18:Unselect(6:12:n_U) @@ -132,7 +127,7 @@ Edge:UP:SOLID|-8:0:1|-18:Unselect(6:12:n_U) Edge:DOWN:SOLID|-8:2:2|-20:Unselect(8:12:n_U) Edge:UP:SOLID|-8:3:4|-9:Unselect(0:9:n_U) Edge:DOWN:SOLID|-8:3:3|-9:Unselect(0:9:n_U) - Simple:UP_ARROW|-8:4|-10:Unselect(0:10:n_U) + Edge:UP_ARROW:SOLID|-8:4:4|-10:Unselect(0:10:n_U) Edge:DOWN:SOLID|-8:4:4|-10:Unselect(0:10:n_U) Edge:UP:SOLID|-9:0:0|-18:Unselect(6:12:n_U) Edge:DOWN:SOLID|-9:0:6|-18:Unselect(6:12:n_U) @@ -143,22 +138,21 @@ Edge:UP:SOLID|-9:0:0|-18:Unselect(6:12:n_U) Simple:NODE|-9:3|-9:Unselect(9_U) Edge:UP:SOLID|-9:3:3|-9:Unselect(0:9:n_U) Edge:DOWN:SOLID|-9:3:9|-21:Unselect(9:12:n_U) - Simple:UP_ARROW|-9:4|-10:Unselect(0:10:n_U) - Edge:UP:SOLID|-9:4:4|-10:Unselect(0:10:n_U) + Edge:UP_ARROW:SOLID|-9:4:4|-10:Unselect(0:10:n_U) Edge:DOWN:SOLID|-9:4:10|-10:Unselect(0:10:n_U) - Simple:UP_ARROW|-9:5|-11:Unselect(0:11:n_U) + Edge:UP_ARROW:SOLID|-9:5:5|-11:Unselect(0:11:n_U) Edge:DOWN:SOLID|-9:5:11|-11:Unselect(0:11:n_U) -Simple:UP_ARROW|-10:0|-12:Unselect(0:12:n_U) +Edge:UP_ARROW:SOLID|-10:0:0|-12:Unselect(0:12:n_U) Edge:DOWN:SOLID|-10:0:0|-12:Unselect(0:12:n_U) - Simple:UP_ARROW|-10:1|-13:Unselect(1:12:n_U) + Edge:UP_ARROW:SOLID|-10:1:1|-13:Unselect(1:12:n_U) Edge:DOWN:SOLID|-10:1:1|-13:Unselect(1:12:n_U) - Simple:UP_ARROW|-10:2|-14:Unselect(2:12:n_U) + Edge:UP_ARROW:SOLID|-10:2:2|-14:Unselect(2:12:n_U) Edge:DOWN:SOLID|-10:2:2|-14:Unselect(2:12:n_U) - Simple:UP_ARROW|-10:3|-15:Unselect(3:12:n_U) + Edge:UP_ARROW:SOLID|-10:3:3|-15:Unselect(3:12:n_U) Edge:DOWN:SOLID|-10:3:3|-15:Unselect(3:12:n_U) - Simple:UP_ARROW|-10:4|-16:Unselect(4:12:n_U) + Edge:UP_ARROW:SOLID|-10:4:4|-16:Unselect(4:12:n_U) Edge:DOWN:SOLID|-10:4:4|-16:Unselect(4:12:n_U) - Simple:UP_ARROW|-10:5|-17:Unselect(5:12:n_U) + Edge:UP_ARROW:SOLID|-10:5:5|-17:Unselect(5:12:n_U) Edge:DOWN:SOLID|-10:5:5|-17:Unselect(5:12:n_U) Edge:UP:SOLID|-10:6:0|-18:Unselect(6:12:n_U) Edge:DOWN:SOLID|-10:6:6|-18:Unselect(6:12:n_U) @@ -171,17 +165,13 @@ Simple:UP_ARROW|-10:0|-12:Unselect(0:12:n_U) Simple:NODE|-10:10|-10:Unselect(10_U) Edge:UP:SOLID|-10:10:4|-10:Unselect(0:10:n_U) Edge:DOWN:SOLID|-10:10:10|-22:Unselect(10:12:n_U) - Simple:UP_ARROW|-10:11|-11:Unselect(0:11:n_U) - Edge:UP:SOLID|-10:11:5|-11:Unselect(0:11:n_U) + Edge:UP_ARROW:SOLID|-10:11:5|-11:Unselect(0:11:n_U) Edge:DOWN:SOLID|-10:11:11|-11:Unselect(0:11:n_U) -Simple:UP_ARROW|-11:0|-12:Unselect(0:12:n_U) - Edge:UP:SOLID|-11:0:0|-12:Unselect(0:12:n_U) +Edge:UP_ARROW:SOLID|-11:0:0|-12:Unselect(0:12:n_U) Edge:DOWN:SOLID|-11:0:0|-12:Unselect(0:12:n_U) - Simple:UP_ARROW|-11:1|-13:Unselect(1:12:n_U) - Edge:UP:SOLID|-11:1:1|-13:Unselect(1:12:n_U) + Edge:UP_ARROW:SOLID|-11:1:1|-13:Unselect(1:12:n_U) Edge:DOWN:SOLID|-11:1:0|-13:Unselect(1:12:n_U) - Simple:UP_ARROW|-11:2|-14:Unselect(2:12:n_U) - Edge:UP:SOLID|-11:2:2|-14:Unselect(2:12:n_U) + Edge:UP_ARROW:SOLID|-11:2:2|-14:Unselect(2:12:n_U) Edge:DOWN:SOLID|-11:2:0|-14:Unselect(2:12:n_U) Edge:UP:SOLID|-11:3:3|-15:Unselect(3:12:n_U) Edge:DOWN:SOLID|-11:3:0|-15:Unselect(3:12:n_U) diff --git a/platform/vcs-log/graph/testData/elementGenerator/specialElements_out.txt b/platform/vcs-log/graph/testData/elementGenerator/specialElements_out.txt index 7267b7f50520..1943c201a48d 100644 --- a/platform/vcs-log/graph/testData/elementGenerator/specialElements_out.txt +++ b/platform/vcs-log/graph/testData/elementGenerator/specialElements_out.txt @@ -2,17 +2,17 @@ Simple:NODE|-0:0|-0:Unselect(0_G) Edge:DOWN:DASHED|-0:0:0|-4:Unselect(0:4:n_D) Edge:DOWN:SOLID|-0:0:1|-0:Unselect(0:n:10_N) Edge:DOWN:DASHED|-0:0:2|-0:Unselect(0:n:11_O) - Simple:UP_ARROW|-0:1|-1:Unselect(n:1:0_P) + Edge:UP_ARROW:DASHED|-0:1:1|-1:Unselect(n:1:0_P) Edge:DOWN:DASHED|-0:1:3|-1:Unselect(n:1:0_P) Edge:UP:DASHED|-1:0:0|-4:Unselect(0:4:n_D) Edge:DOWN:DASHED|-1:0:0|-4:Unselect(0:4:n_D) - Simple:DOWN_ARROW|-1:1|-0:Unselect(0:n:10_N) Edge:UP:SOLID|-1:1:0|-0:Unselect(0:n:10_N) - Simple:DOWN_ARROW|-1:2|-0:Unselect(0:n:11_O) + Edge:DOWN_ARROW:SOLID|-1:1:1|-0:Unselect(0:n:10_N) Edge:UP:DASHED|-1:2:0|-0:Unselect(0:n:11_O) + Edge:DOWN_ARROW:DASHED|-1:2:2|-0:Unselect(0:n:11_O) Simple:NODE|-1:3|-1:Unselect(1_U) Edge:UP:DASHED|-1:3:1|-1:Unselect(n:1:0_P) - Simple:UP_ARROW|-1:4|-2:Unselect(n:2:0_P) + Edge:UP_ARROW:DASHED|-1:4:4|-2:Unselect(n:2:0_P) Edge:DOWN:DASHED|-1:4:1|-2:Unselect(n:2:0_P) Edge:UP:DASHED|-2:0:0|-4:Unselect(0:4:n_D) Edge:DOWN:DASHED|-2:0:0|-4:Unselect(0:4:n_D) @@ -22,10 +22,10 @@ Edge:UP:DASHED|-2:0:0|-4:Unselect(0:4:n_D) Edge:DOWN:SOLID|-2:1:2|-2:Unselect(2:n:12_N) Edge:UP:DASHED|-3:0:0|-4:Unselect(0:4:n_D) Edge:DOWN:DASHED|-3:0:0|-4:Unselect(0:4:n_D) - Simple:DOWN_ARROW|-3:1|-2:Unselect(2:n:3_O) Edge:UP:DASHED|-3:1:1|-2:Unselect(2:n:3_O) - Simple:DOWN_ARROW|-3:2|-2:Unselect(2:n:12_N) + Edge:DOWN_ARROW:DASHED|-3:1:1|-2:Unselect(2:n:3_O) Edge:UP:SOLID|-3:2:1|-2:Unselect(2:n:12_N) + Edge:DOWN_ARROW:SOLID|-3:2:2|-2:Unselect(2:n:12_N) Simple:NODE|-3:3|-3:Unselect(3_G) Simple:NODE|-4:0|-4:Unselect(4_U) Edge:UP:DASHED|-4:0:0|-4:Unselect(0:4:n_D) \ No newline at end of file diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/VcsLogStandardColors.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/VcsLogStandardColors.java new file mode 100644 index 000000000000..3d4caee3ec24 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/VcsLogStandardColors.java @@ -0,0 +1,32 @@ +/* + * 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.vcs.log; + +import com.intellij.ui.ColorUtil; +import com.intellij.ui.JBColor; + +import java.awt.*; + +public class VcsLogStandardColors { + public static class Refs { + public static final Color TIP = new JBColor(new Color(0xedea74), new Color(113, 111, 64)); + public static final Color LEAF = new JBColor(new Color(0xf478cc), new Color(0x6e2857)); + public static final Color BRANCH = new JBColor(new Color(0x75eec7), new Color(0x0D6D4F)); + public static final Color BRANCH_REF = new JBColor(new Color(0xbcbcfc), new Color(0xbcbcfc).darker().darker()); + public static final Color TAG = + new JBColor(ColorUtil.darker(new Color(255, 255, 255), 1), ColorUtil.brighter(new Color(60, 63, 65), 3)); + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManager.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManager.java index a9d942e5085a..6999f9a0f2ba 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManager.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManager.java @@ -23,16 +23,4 @@ public interface VcsLogColorManager { */ boolean isMultipleRoots(); - /** - * Returns the color of the border drawn around a reference label. - */ - @NotNull - Color getReferenceBorderColor(); - - /** - * Returns the color of the border separating the thin root indicator drawn at the left of the commit table. - */ - @NotNull - Color getRootIndicatorBorder(); - } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java index cfce5de26983..0ff5df4bc36e 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java @@ -1,9 +1,11 @@ package com.intellij.vcs.log.ui; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.ColorUtil; import com.intellij.ui.JBColor; +import com.intellij.util.NotNullProducer; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -16,15 +18,10 @@ import java.util.List; * @author Kirill Likhodedov */ public class VcsLogColorManagerImpl implements VcsLogColorManager { - - private static final Color REF_BORDER = JBColor.GRAY; - private static final Color ROOT_INDICATOR_BORDER = JBColor.LIGHT_GRAY; private static final Logger LOG = Logger.getInstance(VcsLogColorManagerImpl.class); - private static Color[] ROOT_COLORS = { - JBColor.RED, JBColor.GREEN, JBColor.BLUE, - JBColor.ORANGE, JBColor.CYAN, JBColor.YELLOW, - JBColor.MAGENTA, JBColor.PINK}; + private static Color[] ROOT_COLORS = + {JBColor.RED, JBColor.GREEN, JBColor.BLUE, JBColor.ORANGE, JBColor.CYAN, JBColor.YELLOW, JBColor.MAGENTA, JBColor.PINK}; @NotNull private final List myRoots; @@ -56,6 +53,30 @@ public class VcsLogColorManagerImpl implements VcsLogColorManager { } } + @NotNull + public static JBColor getBackgroundColor(@NotNull final Color baseRootColor) { + return new JBColor(new NotNullProducer() { + @NotNull + @Override + public Color produce() { + return ColorUtil.mix(baseRootColor, UIUtil.getTableBackground(), 0.75); + } + }); + } + + @NotNull + public static JBColor getIndicatorColor(@NotNull final Color baseRootColor) { + if (Registry.is("vcs.log.square.labels")) return getBackgroundColor(baseRootColor); + return new JBColor(new NotNullProducer() { + @NotNull + @Override + public Color produce() { + if (UIUtil.isUnderDarcula()) return baseRootColor; + return ColorUtil.darker(ColorUtil.softer(baseRootColor), 2); + } + }); + } + @Override public boolean isMultipleRoots() { return myRoots.size() > 1; @@ -75,17 +96,4 @@ public class VcsLogColorManagerImpl implements VcsLogColorManager { private static Color getDefaultRootColor() { return UIUtil.getTableBackground(); } - - @NotNull - @Override - public Color getReferenceBorderColor() { - return REF_BORDER; - } - - @NotNull - @Override - public Color getRootIndicatorBorder() { - return ROOT_INDICATOR_BORDER; - } - } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java index 0a417a5bfb10..9da107563fad 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java @@ -16,8 +16,9 @@ import com.intellij.vcs.log.data.RefsModel; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.impl.SingletonRefGroup; import com.intellij.vcs.log.impl.VcsLogUtil; +import com.intellij.vcs.log.ui.VcsLogColorManagerImpl; import com.intellij.vcs.log.ui.VcsLogUiImpl; -import com.intellij.vcs.log.ui.render.RefPainter; +import com.intellij.vcs.log.ui.render.VcsRefPainter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,19 +28,23 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.util.*; +import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * Panel with branch labels, above the graph. */ public class BranchesPanel extends JPanel { - + private static final int SMALL_ROOTS_GAP = 3; + private static final int BIG_ROOTS_GAP = 7; + private static final int VERTICAL_SPACE = 3; private final VcsLogDataHolder myDataHolder; private final VcsLogUiImpl myUI; - private List myRefGroups; - private final RefPainter myRefPainter; + private LinkedHashMap> myRefGroups; + private final VcsRefPainter myReferencePainter; @Nullable private Collection myRoots = null; private Map myRefPositions = ContainerUtil.newHashMap(); @@ -48,9 +53,9 @@ public class BranchesPanel extends JPanel { myDataHolder = dataHolder; myUI = UI; myRefGroups = getRefsToDisplayOnPanel(initialRefsModel); - myRefPainter = new RefPainter(myUI.getColorManager(), true); + myReferencePainter = new VcsRefPainter(myUI.getColorManager(), true); - setPreferredSize(new Dimension(-1, RefPainter.REF_HEIGHT + UIUtil.DEFAULT_VGAP)); + setPreferredSize(new Dimension(-1, myReferencePainter.getHeight(this) + UIUtil.DEFAULT_VGAP + VERTICAL_SPACE)); addMouseListener(new MouseAdapter() { @Override @@ -68,7 +73,7 @@ public class BranchesPanel extends JPanel { myUI.jumpToCommit(ref.getCommitHash()); } else { - final RefPopupComponent view = new RefPopupComponent(group, myUI, myRefPainter); + final ReferencePopupComponent view = new ReferencePopupComponent(group, myUI, myReferencePainter); JBPopup popup = view.getPopup(); popup.show(new RelativePoint(BranchesPanel.this, new Point(e.getX(), BranchesPanel.this.getHeight()))); } @@ -85,6 +90,11 @@ public class BranchesPanel extends JPanel { }); } + @Override + public Dimension getPreferredSize() { + return new Dimension(-1, myReferencePainter.getHeight(this) + 10); + } + @Nullable private RefGroup findRef(MouseEvent e) { List sortedPositions = Ordering.natural().sortedCopy(myRefPositions.keySet()); @@ -101,16 +111,22 @@ public class BranchesPanel extends JPanel { @Override protected void paintComponent(Graphics g) { myRefPositions = ContainerUtil.newHashMap(); - int paddingX = 0; - for (RefGroup group : myRefGroups) { - // TODO it is assumed here that all refs in a single group belong to a single root - VirtualFile root = group.getRefs().iterator().next().getRoot(); - if (myRoots == null || myRoots.contains(root)) { - Color rootIndicatorColor = myUI.getColorManager().getRootColor(root); - Rectangle rectangle = myRefPainter.drawLabel((Graphics2D)g, group.getName(), paddingX, group.getBgColor(), rootIndicatorColor); - paddingX += rectangle.width + UIUtil.DEFAULT_HGAP; - myRefPositions.put(rectangle.x, group); + int paddingX = BIG_ROOTS_GAP; + for (Map.Entry> entry : myRefGroups.entrySet()) { + VirtualFile root = entry.getKey(); + for (RefGroup group : entry.getValue()) { + // it is assumed here that all refs in a single group belong to a single root + if (myRoots == null || myRoots.contains(root)) { + Color rootIndicatorColor = VcsLogColorManagerImpl.getIndicatorColor(myUI.getColorManager().getRootColor(root)); + Rectangle rectangle = myReferencePainter + .paint(group.getName(), g, paddingX, (getHeight() - myReferencePainter.getHeight(this)) / 2 - VERTICAL_SPACE, + group.getBgColor(), + rootIndicatorColor); + paddingX += rectangle.width + SMALL_ROOTS_GAP; + myRefPositions.put(rectangle.x, group); + } } + paddingX += BIG_ROOTS_GAP - SMALL_ROOTS_GAP; } } @@ -120,16 +136,15 @@ public class BranchesPanel extends JPanel { } @NotNull - private List getRefsToDisplayOnPanel(@NotNull VcsLogRefs refsModel) { + private LinkedHashMap> getRefsToDisplayOnPanel(@NotNull VcsLogRefs refsModel) { Collection allRefs = refsModel.getBranches(); - List groups = ContainerUtil.newArrayList(); + LinkedHashMap> groups = ContainerUtil.newLinkedHashMap(); for (Map.Entry> entry : VcsLogUtil.groupRefsByRoot(allRefs).entrySet()) { VirtualFile root = entry.getKey(); - Collection refs = entry.getValue(); - VcsLogProvider provider = myDataHolder.getLogProvider(root); - VcsLogRefManager refManager = provider.getReferenceManager(); - groups.addAll(expandExpandableGroups(refManager.group(refs))); + List list = ContainerUtil.newArrayList(); + list.addAll(expandExpandableGroups(myDataHolder.getLogProvider(root).getReferenceManager().group(entry.getValue()))); + groups.put(root, list); } return groups; @@ -158,27 +173,22 @@ public class BranchesPanel extends JPanel { getParent().repaint(); } - private static class RefPopupComponent extends JPanel { + private static class ReferencePopupComponent extends JPanel { + @NotNull private final JBPopup myPopup; + @NotNull private final JBList myList; + @NotNull private final VcsLogUiImpl myUi; + @NotNull private final SingleReferenceComponent myRendererComponent; + @NotNull private final ListCellRenderer myCellRenderer; - private final JBPopup myPopup; - private final JBList myList; - private final VcsLogUiImpl myUi; - private final RefPainter myRefPainter; - - private final SingleRefComponent myRendererComponent; - - private final ListCellRenderer myCellRenderer; - - RefPopupComponent(RefGroup group, VcsLogUiImpl ui, RefPainter refPainter) { + ReferencePopupComponent(@NotNull RefGroup group, @NotNull VcsLogUiImpl ui, @NotNull VcsRefPainter referencePainter) { super(new BorderLayout()); myUi = ui; - myRefPainter = refPainter; - myRendererComponent = new SingleRefComponent(myRefPainter); + myRendererComponent = new SingleReferenceComponent(referencePainter); myCellRenderer = new ListCellRenderer() { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - myRendererComponent.setRef((VcsRef)value); + myRendererComponent.setReference((VcsRef)value); myRendererComponent.setSelected(isSelected); return myRendererComponent; } @@ -256,15 +266,16 @@ public class BranchesPanel extends JPanel { } } - private static class SingleRefComponent extends JPanel { + private static class SingleReferenceComponent extends JPanel { + private static final int PADDING_Y = 2; + private static final int PADDING_X = 5; + @NotNull private final VcsRefPainter myReferencePainter; - private final RefPainter myRefPainter; - - private VcsRef myRef; + @Nullable private VcsRef myReference; public boolean mySelected; - public SingleRefComponent(RefPainter refPainter) { - myRefPainter = refPainter; + public SingleReferenceComponent(@NotNull VcsRefPainter referencePainter) { + myReferencePainter = referencePainter; } @Override @@ -272,23 +283,20 @@ public class BranchesPanel extends JPanel { g.setColor(mySelected ? UIUtil.getListSelectionBackground() : UIUtil.getListBackground()); g.fillRect(0, 0, getWidth(), getHeight()); - if (myRef != null) { - myRefPainter.draw((Graphics2D)g, Collections.singletonList(myRef), 0, calcWidth()); + if (myReference != null) { + myReferencePainter.paint(myReference, g, PADDING_X, PADDING_Y); } } @Override public Dimension getPreferredSize() { - return new Dimension(calcWidth(), RefPainter.REF_HEIGHT); + if (myReference == null) return super.getPreferredSize(); + Dimension size = myReferencePainter.getSize(myReference, this); + return new Dimension(size.width + 2 * PADDING_X, size.height + 2 * PADDING_Y); } - private int calcWidth() { - FontMetrics metrics = getFontMetrics(getFont()); - return metrics.stringWidth(myRef.getName()); - } - - public void setRef(@NotNull VcsRef ref) { - myRef = ref; + public void setReference(@NotNull VcsRef reference) { + myReference = reference; } public void setSelected(boolean selected) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java index 4da2b17eddbe..da7d750e4945 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java @@ -43,7 +43,7 @@ import com.intellij.vcs.log.data.LoadingDetails; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.data.VisiblePack; import com.intellij.vcs.log.ui.VcsLogColorManager; -import com.intellij.vcs.log.ui.render.RefPainter; +import com.intellij.vcs.log.ui.render.VcsRefPainter; import com.intellij.vcs.log.ui.tables.GraphTableModel; import net.miginfocom.swing.MigLayout; import org.jetbrains.annotations.NotNull; @@ -80,7 +80,7 @@ class DetailsPanel extends JPanel implements ListSelectionListener { @NotNull private final VcsLogDataHolder myLogDataHolder; @NotNull private final VcsLogGraphTable myGraphTable; - @NotNull private final RefsPanel myRefsPanel; + @NotNull private final ReferencesPanel myReferencesPanel; @NotNull private final DataPanel myCommitDetailsPanel; @NotNull private final MessagePanel myMessagePanel; @NotNull private final JScrollPane myScrollPane; @@ -101,7 +101,7 @@ class DetailsPanel extends JPanel implements ListSelectionListener { myColorManager = colorManager; myDataPack = initialDataPack; - myRefsPanel = new RefsPanel(myColorManager); + myReferencesPanel = new ReferencesPanel(myColorManager); myCommitDetailsPanel = new DataPanel(logDataHolder.getProject(), logDataHolder.isMultiRoot(), logDataHolder); myScrollPane = new JBScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); @@ -117,7 +117,7 @@ class DetailsPanel extends JPanel implements ListSelectionListener { myScrollPane.setOpaque(false); myScrollPane.getViewport().setOpaque(false); myScrollPane.setViewportView(myMainContentPanel); - myMainContentPanel.add(myRefsPanel, ""); + myMainContentPanel.add(myReferencesPanel, ""); myMainContentPanel.add(myCommitDetailsPanel, ""); myLoadingPanel = new JBLoadingPanel(new BorderLayout(), logDataHolder, ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS) { @@ -175,13 +175,13 @@ class DetailsPanel extends JPanel implements ListSelectionListener { if (commitData instanceof LoadingDetails) { myLoadingPanel.startLoading(); myCommitDetailsPanel.setData(null); - myRefsPanel.setRefs(Collections.emptyList()); + myReferencesPanel.setReferences(Collections.emptyList()); updateDetailsBorder(null); } else { myLoadingPanel.stopLoading(); myCommitDetailsPanel.setData(commitData); - myRefsPanel.setRefs(sortRefs(commitData.getId(), commitData.getRoot())); + myReferencesPanel.setReferences(sortRefs(commitData.getId(), commitData.getRoot())); updateDetailsBorder(commitData); newCommitDetails = commitData; } @@ -204,7 +204,7 @@ class DetailsPanel extends JPanel implements ListSelectionListener { myMainContentPanel.setBorder(BorderFactory.createEmptyBorder()); } else { - JBColor color = VcsLogGraphTable.getRootBackgroundColor(data.getRoot(), myColorManager); + Color color = VcsLogGraphTable.getRootBackgroundColor(data.getRoot(), myColorManager); myMainContentPanel.setBorder(new CompoundBorder(new MatteBorder(0, VcsLogGraphTable.ROOT_INDICATOR_COLORED_WIDTH, 0, 0, color), new MatteBorder(0, VcsLogGraphTable.ROOT_INDICATOR_WHITE_WIDTH, 0, 0, new JBColor(new NotNullProducer() { @@ -455,25 +455,24 @@ class DetailsPanel extends JPanel implements ListSelectionListener { } } - private static class RefsPanel extends JPanel { + private static class ReferencesPanel extends JPanel { + @NotNull private final VcsRefPainter myReferencePainter; + @NotNull private List myReferences; - @NotNull private final RefPainter myRefPainter; - @NotNull private List myRefs; - - RefsPanel(@NotNull VcsLogColorManager colorManager) { - super(new FlowLayout(FlowLayout.LEADING, 0, 2)); - myRefPainter = new RefPainter(colorManager, false); - myRefs = Collections.emptyList(); + ReferencesPanel(@NotNull VcsLogColorManager colorManager) { + super(new FlowLayout(FlowLayout.LEADING, 5, 2)); + myReferencePainter = new VcsRefPainter(colorManager, false); + myReferences = Collections.emptyList(); setOpaque(false); } - void setRefs(@NotNull List refs) { + void setReferences(@NotNull List references) { removeAll(); - myRefs = refs; - for (VcsRef ref : refs) { - add(new SingleRefPanel(myRefPainter, ref)); + myReferences = references; + for (VcsRef reference : references) { + add(new SingleReferencePanel(myReferencePainter, reference)); } - setVisible(!myRefs.isEmpty()); + setVisible(!myReferences.isEmpty()); revalidate(); repaint(); } @@ -484,19 +483,19 @@ class DetailsPanel extends JPanel implements ListSelectionListener { } } - private static class SingleRefPanel extends JPanel { - @NotNull private final RefPainter myRefPainter; - @NotNull private VcsRef myRef; + private static class SingleReferencePanel extends JPanel { + @NotNull private final VcsRefPainter myRefPainter; + @NotNull private VcsRef myReference; - SingleRefPanel(@NotNull RefPainter refPainter, @NotNull VcsRef ref) { - myRefPainter = refPainter; - myRef = ref; + SingleReferencePanel(@NotNull VcsRefPainter referencePainter, @NotNull VcsRef reference) { + myRefPainter = referencePainter; + myReference = reference; setOpaque(false); } @Override protected void paintComponent(Graphics g) { - myRefPainter.draw((Graphics2D)g, Collections.singleton(myRef), 0, getWidth()); + myRefPainter.paint(myReference, g, 0, 0); } @Override @@ -506,8 +505,7 @@ class DetailsPanel extends JPanel implements ListSelectionListener { @Override public Dimension getPreferredSize() { - int width = myRefPainter.getLabelWidth(myRef.getName(), getFontMetrics(RefPainter.DEFAULT_FONT)); - return new Dimension(width, RefPainter.REF_HEIGHT + UIUtil.DEFAULT_VGAP); + return myRefPainter.getSize(myReference, this); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/ReferencePopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/ReferencePopupComponent.java new file mode 100644 index 000000000000..7c0ea6fab21e --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/ReferencePopupComponent.java @@ -0,0 +1,167 @@ +/* + * 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.vcs.log.ui.frame; + +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.ui.ListUtil; +import com.intellij.ui.components.JBList; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.util.ui.UIUtil; +import com.intellij.vcs.log.RefGroup; +import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.ui.VcsLogUiImpl; +import com.intellij.vcs.log.ui.render.VcsRefPainter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +class ReferencePopupComponent extends JPanel { + @NotNull private final JBPopup myPopup; + @NotNull private final JBList myList; + @NotNull private final VcsLogUiImpl myUi; + @NotNull private final SingleReferenceComponent myRendererComponent; + @NotNull private final ListCellRenderer myCellRenderer; + + ReferencePopupComponent(@NotNull RefGroup group, @NotNull VcsLogUiImpl ui, @NotNull VcsRefPainter referencePainter) { + super(new BorderLayout()); + myUi = ui; + + myRendererComponent = new SingleReferenceComponent(referencePainter); + myCellRenderer = new ListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + myRendererComponent.setReference((VcsRef)value); + myRendererComponent.setSelected(isSelected); + return myRendererComponent; + } + }; + + myList = createList(group); + myPopup = createPopup(); + add(new JBScrollPane(myList)); + } + + private JBList createList(RefGroup group) { + JBList list = new JBList(createListModel(group)); + list.setCellRenderer(myCellRenderer); + ListUtil.installAutoSelectOnMouseMove(list); + list.setSelectedIndex(0); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + jumpOnMouseClick(list); + jumpOnEnter(list); + + return list; + } + + private void jumpOnMouseClick(JBList list) { + list.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + jumpToSelectedRef(); + } + }); + } + + private void jumpOnEnter(JBList list) { + list.addKeyListener(new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + jumpToSelectedRef(); + } + } + }); + } + + private JBPopup createPopup() { + return JBPopupFactory.getInstance(). + createComponentPopupBuilder(this, myList). + setCancelOnClickOutside(true). + setCancelOnWindowDeactivation(true). + setFocusable(true). + setRequestFocus(true). + setResizable(true). + setDimensionServiceKey(myUi.getProject(), "Vcs.Log.Branch.Panel.RefGroup.Popup", false). + createPopup(); + } + + private static DefaultListModel createListModel(RefGroup group) { + DefaultListModel model = new DefaultListModel(); + for (final VcsRef vcsRef : group.getRefs()) { + model.addElement(vcsRef); + } + return model; + } + + @NotNull + JBPopup getPopup() { + return myPopup; + } + + private void jumpToSelectedRef() { + myPopup.cancel(); // close the popup immediately not to stay at the front if jumping to a commits takes long time. + VcsRef selectedRef = (VcsRef)myList.getSelectedValue(); + if (selectedRef != null) { + myUi.jumpToCommit(selectedRef.getCommitHash()); + } + } + + private static class SingleReferenceComponent extends JPanel { + private static final int PADDING_Y = 2; + private static final int PADDING_X = 5; + @NotNull private final VcsRefPainter myReferencePainter; + + @Nullable private VcsRef myReference; + public boolean mySelected; + + public SingleReferenceComponent(@NotNull VcsRefPainter referencePainter) { + myReferencePainter = referencePainter; + } + + @Override + protected void paintComponent(Graphics g) { + g.setColor(mySelected ? UIUtil.getListSelectionBackground() : UIUtil.getListBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + + if (myReference != null) { + myReferencePainter.paint(myReference, g, PADDING_X, PADDING_Y); + } + } + + @Override + public Dimension getPreferredSize() { + if (myReference == null) return super.getPreferredSize(); + Dimension size = myReferencePainter.getSize(myReference, this); + return new Dimension(size.width + 2 * PADDING_X, size.height + 2 * PADDING_Y); + } + + public void setReference(@NotNull VcsRef reference) { + myReference = reference; + } + + public void setSelected(boolean selected) { + mySelected = selected; + } + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java index 66e2820b01b9..44d8f41bb8d5 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java @@ -31,7 +31,6 @@ import com.intellij.ui.*; import com.intellij.ui.components.JBLabel; import com.intellij.ui.table.JBTable; import com.intellij.util.Function; -import com.intellij.util.NotNullProducer; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; import com.intellij.util.ui.JBUI; @@ -48,10 +47,10 @@ import com.intellij.vcs.log.printer.idea.GraphCellPainter; import com.intellij.vcs.log.printer.idea.PositionUtil; import com.intellij.vcs.log.printer.idea.SimpleGraphCellPainter; import com.intellij.vcs.log.ui.VcsLogColorManager; +import com.intellij.vcs.log.ui.VcsLogColorManagerImpl; import com.intellij.vcs.log.ui.VcsLogUiImpl; import com.intellij.vcs.log.ui.render.GraphCommitCell; import com.intellij.vcs.log.ui.render.GraphCommitCellRender; -import com.intellij.vcs.log.ui.render.RefPainter; import com.intellij.vcs.log.ui.tables.GraphTableModel; import gnu.trove.TIntHashSet; import gnu.trove.TIntProcedure; @@ -92,6 +91,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C } } }; + private final GraphCommitCellRender myGraphCommitCellRenderer; private boolean myColumnsSizeInitialized = false; @@ -113,12 +113,12 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C super(); myUI = UI; myLogDataHolder = logDataHolder; + myGraphCommitCellRenderer = new GraphCommitCellRender(logDataHolder, myGraphCellPainter, this); setDefaultRenderer(VirtualFile.class, new RootCellRenderer(myUI)); - setDefaultRenderer(GraphCommitCell.class, new GraphCommitCellRender(myUI.getColorManager(), logDataHolder, myGraphCellPainter, this)); + setDefaultRenderer(GraphCommitCell.class, myGraphCommitCellRenderer); setDefaultRenderer(String.class, new StringCellRenderer()); - setRowHeight(RefPainter.REF_HEIGHT); setShowHorizontalLines(false); setIntercellSpacing(JBUI.emptySize()); @@ -410,14 +410,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C } public static JBColor getRootBackgroundColor(@NotNull VirtualFile root, @NotNull VcsLogColorManager colorManager) { - final Color rootColor = colorManager.getRootColor(root); - return new JBColor(new NotNullProducer() { - @NotNull - @Override - public Color produce() { - return ColorUtil.mix(rootColor, UIUtil.getTableBackground(), 0.75); - } - }); + return VcsLogColorManagerImpl.getBackgroundColor(colorManager.getRootColor(root)); } public void handleAnswer(@Nullable GraphAnswer answer, boolean dataCouldChange, @Nullable Selection previousSelection) { @@ -758,6 +751,11 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C return myDummyEditor; } + @Override + public int getRowHeight() { + return myGraphCommitCellRenderer.getPreferredHeight(); + } + private class StringCellRenderer extends ColoredTableCellRenderer { @Override protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRender.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRender.java index 59ed3b25720d..c9fdbe6dba94 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRender.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRender.java @@ -12,7 +12,6 @@ import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.graph.PrintElement; import com.intellij.vcs.log.printer.idea.GraphCellPainter; import com.intellij.vcs.log.printer.idea.PrintParameters; -import com.intellij.vcs.log.ui.VcsLogColorManager; import com.intellij.vcs.log.ui.frame.VcsLogGraphTable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,34 +31,46 @@ public class GraphCommitCellRender extends ColoredTableCellRenderer { @NotNull private final VcsLogDataHolder myDataHolder; @NotNull private final GraphCellPainter myPainter; @NotNull private final VcsLogGraphTable myGraphTable; - @NotNull private final RefPainter myRefPainter; + @NotNull private final TextLabelPainter myTextLabelPainter; @NotNull private final IssueLinkRenderer myIssueLinkRenderer; @Nullable private PaintInfo myGraphImage; @Nullable private Collection myRefs; - public GraphCommitCellRender(@NotNull VcsLogColorManager colorManager, - @NotNull VcsLogDataHolder dataHolder, + public GraphCommitCellRender(@NotNull VcsLogDataHolder dataHolder, @NotNull GraphCellPainter painter, @NotNull VcsLogGraphTable table) { myDataHolder = dataHolder; myPainter = painter; myGraphTable = table; - myRefPainter = new RefPainter(colorManager, false) { - @Override - protected int getRowHeight() { - return myGraphTable.getRowHeight(); - } - }; + myTextLabelPainter = TextLabelPainter.createPainter(false); myIssueLinkRenderer = new IssueLinkRenderer(dataHolder.getProject(), this); } + @NotNull + @Override + public Dimension getPreferredSize() { + Dimension preferredSize = super.getPreferredSize(); + return new Dimension(preferredSize.width, getPreferredHeight()); + } + + public int getPreferredHeight() { + return myTextLabelPainter.calculateSize("", getFontMetrics(TextLabelPainter.getFont())).height + 4; + } + @Override public void paintComponent(Graphics g) { super.paintComponent(g); if (myRefs != null) { - myRefPainter.drawLabels((Graphics2D)g, collectLabelsForRefs(myRefs), myGraphImage != null ? myGraphImage.getWidth() : 0); + int paddingX = (myGraphImage != null ? myGraphImage.getWidth() : 0) + PrintParameters.LABEL_PADDING; + Map labelsForReferences = collectLabelsForRefs(myRefs); + for (Map.Entry entry : labelsForReferences.entrySet()) { + Dimension size = myTextLabelPainter.calculateSize(entry.getKey(), g.getFontMetrics(TextLabelPainter.getFont())); + int paddingY = (myGraphTable.getRowHeight() - size.height) / 2; + myTextLabelPainter.paint((Graphics2D)g, entry.getKey(), paddingX, paddingY, entry.getValue()); + paddingX += size.width + PrintParameters.LABEL_PADDING; + } } if (myGraphImage != null) { @@ -90,7 +101,7 @@ public class GraphCommitCellRender extends ColoredTableCellRenderer { else { graphPadding = 0; } - int textPadding = graphPadding + calcRefsPadding(myRefs); + int textPadding = graphPadding + calculateReferencePadding(myRefs); setBorder(null); append(""); @@ -132,8 +143,15 @@ public class GraphCommitCellRender extends ColoredTableCellRenderer { return getLabelsForRefs(branches, tags); } - private int calcRefsPadding(@NotNull Collection refs) { - return myRefPainter.getLabelsWidth(collectLabelsForRefs(refs).keySet(), this.getFontMetrics(RefPainter.DEFAULT_FONT)); + private int calculateReferencePadding(@NotNull Collection references) { + if (references.isEmpty()) return 0; + + int paddingX = 2 * PrintParameters.LABEL_PADDING; + for (String label : collectLabelsForRefs(references).keySet()) { + Dimension size = myTextLabelPainter.calculateSize(label, this.getFontMetrics(TextLabelPainter.getFont())); + paddingX += size.width + PrintParameters.LABEL_PADDING; + } + return paddingX; } @NotNull diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/RefPainter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/RefPainter.java deleted file mode 100644 index ffec0d4b87a1..000000000000 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/RefPainter.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.intellij.vcs.log.ui.render; - -import com.intellij.ui.JBColor; -import com.intellij.vcs.log.VcsRef; -import com.intellij.vcs.log.ui.VcsLogColorManager; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.awt.*; -import java.awt.font.FontRenderContext; -import java.awt.geom.RoundRectangle2D; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * @author erokhins - */ -public class RefPainter { - private static final int RECTANGLE_X_PADDING = 4; - private static final int RECTANGLE_Y_PADDING = 3; - private static final int REF_PADDING = 13; - private static final int LABEL_PADDING = 3; - - private static final int FLAG_WIDTH = 8; - private static final int FLAG_PADDING = 8; - private static final int FLAG_TOP_INDENT = 2; - - private static final int ROUND_RADIUS = 5; - - public static final Font DEFAULT_FONT = new Font("Arial", Font.PLAIN, 12); // wtf, shouldn't we use ui font? - private static final Color DEFAULT_FONT_COLOR = JBColor.BLACK; - public static final int REF_HEIGHT = 22; - - @NotNull private final VcsLogColorManager myColorManager; - private final boolean myDrawMultiRepoIndicator; - - public RefPainter(@NotNull VcsLogColorManager manager, boolean drawMultiRepoIndicator) { - myColorManager = manager; - myDrawMultiRepoIndicator = drawMultiRepoIndicator; - } - - protected int getRowHeight() { - return REF_HEIGHT; - } - - public int getLabelWidth(@NotNull String str, FontMetrics metrics) { - return getHorizontalDimensions(str, 0, metrics).width + LABEL_PADDING; - } - - public int getLabelsWidth(@NotNull Collection refs, FontMetrics fontMetrics) { - if (refs.isEmpty()) return 0; - - int p = 2 * LABEL_PADDING; // additional padding after all references looks better - for (String ref : refs) { - HorizontalDimensions horizontalDimensions = getHorizontalDimensions(ref, p, fontMetrics); - p += horizontalDimensions.width + LABEL_PADDING; - } - - return p; - } - - public Map draw(@NotNull Graphics2D g2, @NotNull Collection refs, int startPadding, int maxWidth) { - float currentPadding = startPadding; - setupGraphics(g2); - FontRenderContext renderContext = g2.getFontRenderContext(); - Map positions = new HashMap(); - for (VcsRef ref : refs) { - int x = - drawLabel(g2, ref.getName(), (int)currentPadding, ref.getType().getBackgroundColor(), myColorManager.getRootColor(ref.getRoot())).x; - positions.put(x, ref); - currentPadding += paddingStr(ref.getName(), renderContext); - if (maxWidth > 0 && x >= maxWidth) { - break; - } - } - return positions; - } - - public void drawLabels(@NotNull Graphics2D g2, @NotNull Map labels, int startPadding) { - int padding = startPadding; - for (Map.Entry entry : labels.entrySet()) { - Rectangle rectangle = drawLabel(g2, entry.getKey(), padding, entry.getValue(), null); - padding += rectangle.width + LABEL_PADDING; - } - } - - public Rectangle drawLabel(@NotNull Graphics2D g2, - @NotNull String label, - int paddingX, - @NotNull Color bgColor, - @Nullable Color rootIndicatorColor) { - setupGraphics(g2); - FontMetrics metrics = g2.getFontMetrics(); - HorizontalDimensions horizontalDimensions = getHorizontalDimensions(label, paddingX, metrics); - int x = horizontalDimensions.x; - int width = horizontalDimensions.width; - int y = RECTANGLE_Y_PADDING + (getRowHeight() - REF_HEIGHT) / 2; - int height = REF_HEIGHT - 2 * RECTANGLE_Y_PADDING; - RoundRectangle2D rectangle2D = new RoundRectangle2D.Double(x, y, width, height, ROUND_RADIUS, ROUND_RADIUS); - - g2.setColor(bgColor); - g2.fill(rectangle2D); - - g2.setColor(myColorManager.getReferenceBorderColor()); - g2.draw(rectangle2D); - - g2.setColor(JBColor.BLACK); - drawText(g2, label, paddingX + flagWidth(), (getRowHeight() - REF_HEIGHT) / 2); - - if (rootIndicatorColor != null && myColorManager.isMultipleRoots() && myDrawMultiRepoIndicator) { - drawRootIndicator(g2, paddingX, y, height, rootIndicatorColor); - } - - return new Rectangle(x, y, width, height); - } - - private void drawRootIndicator(@NotNull Graphics2D g2, int x, int y, int height, @NotNull Color rootIndicatorColor) { - g2.setColor(rootIndicatorColor); - int x0 = x + FLAG_PADDING; - int xMid = x0 + FLAG_WIDTH / 2; - int xRight = x0 + FLAG_WIDTH; - - int y0 = y - FLAG_TOP_INDENT; - int yMid = y0 + 2 * height / 3 - 2; - int yBottom = y0 + height - 4; - - // something like a knight flag - Polygon polygon = new Polygon(new int[]{x0, xRight, xRight, xMid, x0}, new int[]{y0, y0, yMid, yBottom, yMid}, 5); - g2.fillPolygon(polygon); - g2.setColor(myColorManager.getReferenceBorderColor()); - g2.drawPolygon(polygon); - } - - private HorizontalDimensions getHorizontalDimensions(String label, int paddingX, FontMetrics metrics) { - int x = paddingX + REF_PADDING / 2 - RECTANGLE_X_PADDING; - int width = metrics.stringWidth(label) + 2 * RECTANGLE_X_PADDING + flagWidth(); - return new HorizontalDimensions(x, width); - } - - private double paddingStr(@NotNull String str, @NotNull FontRenderContext renderContext) { - return getStringWidth(str, renderContext) + REF_PADDING + flagWidth(); - } - - private int flagWidth() { - return myColorManager.isMultipleRoots() && myDrawMultiRepoIndicator ? FLAG_WIDTH + FLAG_PADDING : 0; - } - - private static double getStringWidth(@NotNull String str, @NotNull FontRenderContext renderContext) { - return DEFAULT_FONT.getStringBounds(str, renderContext).getWidth(); - } - - private static void drawText(@NotNull Graphics2D g2, @NotNull String str, int paddingX, int paddingY) { - FontMetrics metrics = g2.getFontMetrics(); - g2.setColor(DEFAULT_FONT_COLOR); - int x = paddingX + REF_PADDING / 2; - int y = paddingY + REF_HEIGHT / 2 + (metrics.getAscent() - metrics.getDescent()) / 2; - g2.drawString(str, x, y); - } - - private static void setupGraphics(Graphics2D g2) { - g2.setFont(DEFAULT_FONT); - g2.setStroke(new BasicStroke(1.5f)); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - } - - private static class HorizontalDimensions { - private final int x; - private final int width; - - private HorizontalDimensions(int x, int width) { - this.x = x; - this.width = width; - } - } - -} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/TextLabelPainter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/TextLabelPainter.java new file mode 100644 index 000000000000..de7759e2b443 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/TextLabelPainter.java @@ -0,0 +1,177 @@ +/* + * 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.vcs.log.ui.render; + +import com.intellij.openapi.util.registry.Registry; +import com.intellij.ui.JBColor; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; +import java.awt.geom.RoundRectangle2D; + +public class TextLabelPainter { + protected static final int TEXT_PADDING_X = 5; + protected static final int TOP_TEXT_PADDING = 2; + protected static final int BOTTOM_TEXT_PADDING = 1; + + private static final int LABEL_ARC = 5; + + private final boolean mySquare; + + public TextLabelPainter(boolean square) { + mySquare = square; + } + + public static TextLabelPainter createPainter(boolean paintFlag) { + boolean square = Registry.is("vcs.log.square.labels"); + if (paintFlag) { + return square ? new UnderlinedLabelPainter() : new FlagLabelPainter(); + } + else { + return new TextLabelPainter(square); + } + } + + public static Font getFont() { + return UIUtil.getLabelFont(); + } + + protected Rectangle paint(@NotNull Graphics2D g2, + @NotNull String text, + int paddingX, + int paddingY, + int textPadding, + @NotNull Color color) { + g2.setFont(getFont()); + g2.setStroke(new BasicStroke(1.5f)); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + FontMetrics fontMetrics = g2.getFontMetrics(); + int width = fontMetrics.stringWidth(text) + 2 * TEXT_PADDING_X + textPadding; + int height = fontMetrics.getHeight() + TOP_TEXT_PADDING + BOTTOM_TEXT_PADDING; + + g2.setColor(color); + if (mySquare) { + g2.fillRect(paddingX, paddingY, width, height); + } + else { + g2.fill(new RoundRectangle2D.Double(paddingX, paddingY, width, height, LABEL_ARC, LABEL_ARC)); + } + + g2.setColor(JBColor.BLACK); + int x = paddingX + textPadding + TEXT_PADDING_X; + int y = paddingY + SimpleColoredComponent.getTextBaseLine(fontMetrics, height); + g2.drawString(text, x, y); + + return new Rectangle(x, y, width, height); + } + + public Rectangle paint(@NotNull Graphics2D g2, @NotNull String text, int paddingX, int paddingY, @NotNull Color color) { + return paint(g2, text, paddingX, paddingY, 0, color); + } + + public Rectangle paint(@NotNull Graphics2D g2, + @NotNull String text, + int paddingX, + int paddingY, + @NotNull Color labelColor, + @NotNull Color flagColor) { + return paint(g2, text, paddingX, paddingY, 0, labelColor); + } + + public Dimension calculateSize(@NotNull String text, @NotNull FontMetrics metrics) { + int width = metrics.stringWidth(text) + 2 * TEXT_PADDING_X; + int height = metrics.getHeight() + TOP_TEXT_PADDING + BOTTOM_TEXT_PADDING; + return new Dimension(width, height); + } + + private static class FlagLabelPainter extends TextLabelPainter { + private static final int FLAG_WIDTH = 8; + private static final int FLAG_PADDING = 6; + private static final int FLAG_TOP_INDENT = 2; + + public FlagLabelPainter() { + super(false); + } + + @Override + public Rectangle paint(@NotNull Graphics2D g2, + @NotNull String text, + int paddingX, + int paddingY, + @NotNull Color labelColor, + @NotNull Color flagColor) { + + Rectangle rectangle = + super.paint(g2, text, paddingX, paddingY + FLAG_TOP_INDENT, FLAG_WIDTH + 2 * FLAG_PADDING - TEXT_PADDING_X, labelColor); + + g2.setColor(flagColor); + int x0 = paddingX + FLAG_PADDING; + int xMid = x0 + FLAG_WIDTH / 2; + int xRight = x0 + FLAG_WIDTH; + + int y0 = paddingY; + int yMid = y0 + 2 * rectangle.height / 3 - 2; + int yBottom = y0 + rectangle.height - 4; + + // something like a knight flag + Polygon polygon = new Polygon(new int[]{x0, xRight, xRight, xMid, x0}, new int[]{y0, y0, yMid, yBottom, yMid}, 5); + g2.fillPolygon(polygon); + + return new Rectangle(paddingX, paddingY, rectangle.width, rectangle.height + FLAG_TOP_INDENT); + } + + @Override + public Dimension calculateSize(@NotNull String text, @NotNull FontMetrics metrics) { + Dimension size = super.calculateSize(text, metrics); + return new Dimension(size.width + FLAG_WIDTH + 2 * FLAG_PADDING - TEXT_PADDING_X, size.height + FLAG_TOP_INDENT); + } + } + + private static class UnderlinedLabelPainter extends TextLabelPainter { + + private static final int LINE_GAP = 1; + private static final int LINE_HEIGHT = 4; + + public UnderlinedLabelPainter() { + super(true); + } + + @Override + public Rectangle paint(@NotNull Graphics2D g2, + @NotNull String text, + int paddingX, + int paddingY, + @NotNull Color labelColor, + @NotNull Color flagColor) { + + Rectangle rectangle = super.paint(g2, text, paddingX, paddingY, 0, labelColor); + + g2.setColor(flagColor); + g2.fillRect(paddingX, paddingY + rectangle.height + LINE_GAP, rectangle.width, LINE_HEIGHT); + + return new Rectangle(paddingX, paddingY, rectangle.width, rectangle.height + LINE_GAP + LINE_HEIGHT); + } + + @Override + public Dimension calculateSize(@NotNull String text, @NotNull FontMetrics metrics) { + Dimension size = super.calculateSize(text, metrics); + return new Dimension(size.width, size.height + LINE_GAP + LINE_HEIGHT); + } + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/VcsRefPainter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/VcsRefPainter.java new file mode 100644 index 000000000000..a2c2becc5812 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/VcsRefPainter.java @@ -0,0 +1,60 @@ +/* + * 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.vcs.log.ui.render; + +import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.ui.VcsLogColorManager; +import com.intellij.vcs.log.ui.VcsLogColorManagerImpl; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; + +public class VcsRefPainter { + @NotNull private final VcsLogColorManager myColorManager; + @NotNull private final TextLabelPainter myTextLabelPainter; + + public VcsRefPainter(@NotNull VcsLogColorManager colorManager, boolean paintRoot) { + myColorManager = colorManager; + myTextLabelPainter = TextLabelPainter.createPainter(paintRoot && colorManager.isMultipleRoots()); + } + + public void paint(@NotNull VcsRef reference, @NotNull Graphics g, int paddingX, int paddingY) { + myTextLabelPainter.paint((Graphics2D)g, reference.getName(), paddingX, paddingY, reference.getType().getBackgroundColor(), + VcsLogColorManagerImpl.getIndicatorColor(myColorManager.getRootColor(reference.getRoot()))); + } + + public Rectangle paint(@NotNull String text, + @NotNull Graphics g, + int paddingX, + int paddingY, + @NotNull Color color, + @NotNull Color flagColor) { + return myTextLabelPainter.paint((Graphics2D)g, text, paddingX, paddingY, color, flagColor); + } + + public Dimension getSize(@NotNull VcsRef reference, @NotNull JComponent component) { + return getSize(reference.getName(), component); + } + + public Dimension getSize(@NotNull String referenceName, @NotNull JComponent component) { + return myTextLabelPainter.calculateSize(referenceName, component.getFontMetrics(TextLabelPainter.getFont())); + } + + public int getHeight(@NotNull JComponent component) { + return myTextLabelPainter.calculateSize("", component.getFontMetrics(TextLabelPainter.getFont())).height; + } +} diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java b/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java index c8cf928e126c..6116d15b2ceb 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java @@ -136,7 +136,7 @@ public abstract class XDebugProcess { } @NotNull - public Promise stopAsync() { + public Promise stopAsync() { stop(); return Promise.DONE; } diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/presentation/XErrorValuePresentation.java b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/presentation/XErrorValuePresentation.java new file mode 100644 index 000000000000..16b9025d8973 --- /dev/null +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/presentation/XErrorValuePresentation.java @@ -0,0 +1,36 @@ +/* + * 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.xdebugger.frame.presentation; + +import org.jetbrains.annotations.NotNull; + +/** + * Renders a value as an error + * + * @author egor + */ +public class XErrorValuePresentation extends XValuePresentation { + private final String myError; + + public XErrorValuePresentation(@NotNull String error) { + myError = error; + } + + @Override + public void renderValue(@NotNull XValueTextRenderer renderer) { + renderer.renderError(myError); + } +} diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java index 9fe07c710dab..91c788c742c8 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java @@ -867,9 +867,10 @@ public class XDebugSessionImpl implements XDebugSession { return; } - myDebugProcess.stopAsync().done(new Consumer() { + //noinspection unchecked + myDebugProcess.stopAsync().done(new Consumer() { @Override - public void consume(Void aVoid) { + public void consume(Object aVoid) { if (!myProject.isDisposed()) { myProject.getMessageBus().syncPublisher(XDebuggerManager.TOPIC).processStopped(myDebugProcess); } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java index fed4cdc556c7..42dd8cf64839 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java @@ -34,6 +34,7 @@ import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters; import com.intellij.util.xmlb.XmlSerializer; import com.intellij.util.xmlb.annotations.AbstractCollection; import com.intellij.util.xmlb.annotations.Tag; +import com.intellij.xdebugger.XDebuggerUtil; import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.breakpoints.*; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; @@ -262,7 +263,9 @@ public class XBreakpointManagerImpl implements XBreakpointManager, PersistentSta @NotNull @Override public > Collection getBreakpoints(@NotNull Class> typeClass) { - return getBreakpoints(XBreakpointType.EXTENSION_POINT_NAME.findExtension(typeClass)); + XBreakpointType type = XDebuggerUtil.getInstance().findBreakpointType(typeClass); + LOG.assertTrue(type != null, "Unregistered breakpoint type " + typeClass); + return getBreakpoints(type); } @Override diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.form b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.form index af0292479091..f06e524caae9 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.form +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.form @@ -27,7 +27,7 @@ - + diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.java index 4edcb9b1aa5e..5620fa509297 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.java @@ -48,6 +48,7 @@ public class XBreakpointActionsPanel> extends X private JPanel myContentPane; private JPanel myMainPanel; private JCheckBox myTemporaryCheckBox; + private JPanel myExpressionPanel; private XDebuggerExpressionComboBox myLogExpressionComboBox; public void init(Project project, XBreakpointManager breakpointManager, @NotNull B breakpoint, @Nullable XDebuggerEditorsProvider debuggerEditorsProvider) { @@ -67,7 +68,7 @@ public class XBreakpointActionsPanel> extends X DebuggerUIUtil.focusEditorOnCheck(myLogExpressionCheckBox, myLogExpressionComboBox.getEditorComponent()); } else { - myLogExpressionCheckBox.setVisible(false); + myExpressionPanel.getParent().remove(myExpressionPanel); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XLightBreakpointPropertiesPanel.form b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XLightBreakpointPropertiesPanel.form index 4ce06a6b52da..36eab3242ab9 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XLightBreakpointPropertiesPanel.form +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XLightBreakpointPropertiesPanel.form @@ -1,38 +1,35 @@
    - + - + - + - - + - + - + + + - - - - + + - - - - + + @@ -41,22 +38,22 @@ - + - + - + - + @@ -64,7 +61,7 @@ - + @@ -72,24 +69,15 @@ - + - - - - - - - - - - + @@ -100,17 +88,44 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XLightBreakpointPropertiesPanel.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XLightBreakpointPropertiesPanel.java index 810b5558a6cb..882a2c1bc36e 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XLightBreakpointPropertiesPanel.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XLightBreakpointPropertiesPanel.java @@ -20,7 +20,7 @@ import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.components.JBLabel; import com.intellij.ui.popup.util.DetailView; -import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebuggerBundle; import com.intellij.xdebugger.XExpression; import com.intellij.xdebugger.breakpoints.XBreakpointManager; @@ -89,6 +89,7 @@ public class XLightBreakpointPropertiesPanel> i private JBCheckBox myConditionEnabledCheckbox; private JPanel myCustomTopPropertiesPanelWrapper; private JPanel myConditionEnabledPanel; + private JBLabel myBreakpointNameLabel; private final List> myCustomPanels; private final List> mySubPanels = new ArrayList>(); @@ -98,8 +99,6 @@ public class XLightBreakpointPropertiesPanel> i private final B myBreakpoint; private final boolean myShowAllOptions; - private static final String CONDITION_ENABLED_LABEL = "label"; - private static final String CONDITION_ENABLED_CHECKBOX = "checkbox"; public void setDetailView(DetailView detailView) { myMasterBreakpointPanel.setDetailView(detailView); @@ -140,12 +139,9 @@ public class XLightBreakpointPropertiesPanel> i myCustomPanels = new ArrayList>(); if (debuggerEditorsProvider != null) { myConditionEnabledCheckbox = new JBCheckBox(XDebuggerBundle.message("xbreakpoints.condition.checkbox")); - JBLabel conditionEnabledLabel = new JBLabel(XDebuggerBundle.message("xbreakpoints.condition.checkbox")); - conditionEnabledLabel.setBorder(UIUtil.getTextAlignBorder(myConditionEnabledCheckbox)); - myConditionEnabledPanel.add(myConditionEnabledCheckbox, CONDITION_ENABLED_CHECKBOX); - myConditionEnabledPanel.add(conditionEnabledLabel, CONDITION_ENABLED_LABEL); myConditionComboBox = new XDebuggerExpressionComboBox(project, debuggerEditorsProvider, CONDITION_HISTORY_ID, myBreakpoint.getSourcePosition()); JComponent conditionComponent = myConditionComboBox.getComponent(); + conditionComponent.setBorder(JBUI.Borders.emptyRight(3)); myConditionExpressionPanel.add(conditionComponent, BorderLayout.CENTER); myConditionEnabledCheckbox.addActionListener(new ActionListener() { @Override @@ -170,12 +166,18 @@ public class XLightBreakpointPropertiesPanel> i myCustomPropertiesPanelWrapper.add(customPropertiesPanel.getComponent(), BorderLayout.CENTER); myCustomPanels.add(customPropertiesPanel); } + else { + myCustomPropertiesPanelWrapper.getParent().remove(myCustomPropertiesPanelWrapper); + } XBreakpointCustomPropertiesPanel customConditionPanel = breakpointType.createCustomConditionsPanel(); if (customConditionPanel != null) { myCustomConditionsPanelWrapper.add(customConditionPanel.getComponent(), BorderLayout.CENTER); myCustomPanels.add(customConditionPanel); } + else { + myCustomConditionsPanelWrapper.getParent().remove(myCustomConditionsPanelWrapper); + } XBreakpointCustomPropertiesPanel customRightConditionPanel = breakpointType.createCustomRightPropertiesPanel(project); if (customRightConditionPanel != null && (showAllOptions || customRightConditionPanel.isVisibleOnPopup(breakpoint))) { @@ -192,6 +194,9 @@ public class XLightBreakpointPropertiesPanel> i myCustomTopPropertiesPanelWrapper.add(customTopPropertiesPanel.getComponent(), BorderLayout.CENTER); myCustomPanels.add(customTopPropertiesPanel); } + else { + myCustomTopPropertiesPanelWrapper.getParent().remove(myCustomTopPropertiesPanelWrapper); + } myMainPanel.addFocusListener(new FocusAdapter() { @Override @@ -252,7 +257,15 @@ public class XLightBreakpointPropertiesPanel> i myConditionComboBox.setExpression(condition); boolean hideCheckbox = !myShowAllOptions && condition == null; myConditionEnabledCheckbox.setSelected(hideCheckbox || (myBreakpoint.isConditionEnabled() && condition != null)); - ((CardLayout)myConditionEnabledPanel.getLayout()).show(myConditionEnabledPanel, hideCheckbox ? CONDITION_ENABLED_LABEL : CONDITION_ENABLED_CHECKBOX); + myConditionEnabledPanel.removeAll(); + if (hideCheckbox) { + JBLabel label = new JBLabel(XDebuggerBundle.message("xbreakpoints.condition.checkbox")); + label.setBorder(JBUI.Borders.empty(0, 4)); + myConditionEnabledPanel.add(label); + } + else { + myConditionEnabledPanel.add(myConditionEnabledCheckbox); + } onCheckboxChanged(); } @@ -261,7 +274,7 @@ public class XLightBreakpointPropertiesPanel> i customPanel.loadFrom(myBreakpoint); } myEnabledCheckbox.setSelected(myBreakpoint.isEnabled()); - myEnabledCheckbox.setText(XBreakpointUtil.getShortText(myBreakpoint) + " enabled"); + myBreakpointNameLabel.setText(XBreakpointUtil.getShortText(myBreakpoint)); } public JPanel getMainPanel() { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XMasterBreakpointPanel.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XMasterBreakpointPanel.java index b2e3bab65713..4d3f4b3367c0 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XMasterBreakpointPanel.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XMasterBreakpointPanel.java @@ -112,6 +112,6 @@ public class XMasterBreakpointPanel> extends XBreakpoin } public void hide() { - myContentPane.setVisible(false); + myContentPane.getParent().remove(myContentPane); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XSuspendPolicyPanel.form b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XSuspendPolicyPanel.form index 9f1a743cf0ea..7bafcedf0304 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XSuspendPolicyPanel.form +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XSuspendPolicyPanel.form @@ -3,7 +3,7 @@ - + @@ -39,14 +39,32 @@ - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XSuspendPolicyPanel.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XSuspendPolicyPanel.java index b83cfa4d3193..6d2e0ee00932 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XSuspendPolicyPanel.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XSuspendPolicyPanel.java @@ -36,6 +36,7 @@ public class XSuspendPolicyPanel> extends XBreakpointPr private JPanel myContentPane; private JButton myMakeDefaultButton; + private JPanel myMakeDefaultPanel; private ButtonGroup mySuspendPolicyGroup; @@ -99,7 +100,11 @@ public class XSuspendPolicyPanel> extends XBreakpointPr } private void updateMakeDefaultEnableState() { - myMakeDefaultButton.setEnabled(!getSelectedSuspendPolicy().equals(((XBreakpointManagerImpl)myBreakpointManager).getBreakpointDefaults(myBreakpointType).getSuspendPolicy())); + boolean enabled = !getSelectedSuspendPolicy().equals( + ((XBreakpointManagerImpl)myBreakpointManager).getBreakpointDefaults(myBreakpointType).getSuspendPolicy()); + ((CardLayout)myMakeDefaultPanel.getLayout()).show(myMakeDefaultPanel, enabled ? "Show" : "Hide"); + myMakeDefaultButton.setVisible(enabled); + myMakeDefaultButton.setEnabled(enabled); } private void updateSuspendPolicyFont() { @@ -125,7 +130,7 @@ public class XSuspendPolicyPanel> extends XBreakpointPr private void changeVisibleState(boolean suspendThreadSupported) { mySuspendAll.setVisible(suspendThreadSupported); mySuspendThread.setVisible(suspendThreadSupported); - myMakeDefaultButton.setVisible(suspendThreadSupported); + myMakeDefaultPanel.setVisible(suspendThreadSupported); } @Override @@ -135,7 +140,7 @@ public class XSuspendPolicyPanel> extends XBreakpointPr } public void hide() { - myContentPane.setVisible(false); + myContentPane.getParent().remove(myContentPane); } @Override diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javabeans/SuspiciousGetterSetterInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javabeans/SuspiciousGetterSetterInspection.java index 44a71f3a7cb7..114e5d184195 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javabeans/SuspiciousGetterSetterInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javabeans/SuspiciousGetterSetterInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -105,8 +105,10 @@ public class SuspiciousGetterSetterInspection extends BaseInspection { return; } final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(method.getProject()); - final String computedFieldName = codeStyleManager.propertyNameToVariableName(decapitalize(extractedFieldName), VariableKind.FIELD); - if (fieldName.equalsIgnoreCase(computedFieldName)) { + final String decapitalized = decapitalize(extractedFieldName); + final String computedFieldName = codeStyleManager.propertyNameToVariableName(decapitalized, VariableKind.FIELD); + final String computedStaticFieldName = codeStyleManager.propertyNameToVariableName(decapitalized, VariableKind.STATIC_FINAL_FIELD); + if (fieldName.equals(computedFieldName) || fieldName.equals(computedStaticFieldName)) { return; } if (onlyWarnWhenFieldPresent) { @@ -114,7 +116,8 @@ public class SuspiciousGetterSetterInspection extends BaseInspection { if (aClass == null) { return; } - if (aClass.findFieldByName(computedFieldName, true) == null) { + if (aClass.findFieldByName(computedFieldName, true) == null && + aClass.findFieldByName(computedStaticFieldName, true) == null) { return; } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java index 0e91b06c3b6f..58266fae1716 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java @@ -143,10 +143,12 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { separator = true; } int j = 1; - while (!unwantedChildren.contains(Integer.valueOf(j)) && j < tryBlockChildren.length - 1) { - tryStatement.getParent().addBefore(tryBlockChildren[j], tryStatement); - unwantedChildren.add(j); - j++; + if (!unwantedChildren.isEmpty()) { + while (!unwantedChildren.contains(Integer.valueOf(j)) && j < tryBlockChildren.length - 1) { + tryStatement.getParent().addBefore(tryBlockChildren[j], tryStatement); + unwantedChildren.add(j); + j++; + } } newTryStatementText.append(") {"); final int tryBlockStatementsLength = tryBlockChildren.length - 1; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionBase.java index d0ce6de63884..6bd74419d834 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionBase.java @@ -43,8 +43,9 @@ public class BooleanMethodNameMustStartWithQuestionInspectionBase extends BaseIn /** * @noinspection PublicField */ - @NonNls public String questionString = "is,can,has,should,could,will,shall,check,contains,equals,add,put,remove,startsWith,endsWith";List - questionList = new ArrayList(32); + @NonNls public String questionString = + "add,are,can,check,contains,could,endsWith,equals,has,is,matches,must,put,remove,shall,should,startsWith,was,were,will,would"; + List questionList = new ArrayList(32); public BooleanMethodNameMustStartWithQuestionInspectionBase() { parseString(questionString, questionList); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspectionBase.java index 80d9fbb7fc1f..2eefa678699d 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspectionBase.java @@ -40,7 +40,8 @@ public class NonBooleanMethodNameMayNotStartWithQuestionInspectionBase extends B /** * @noinspection PublicField */ - @NonNls public String questionString = "is,can,has,should,could,will,shall,check,contains,equals,startsWith,endsWith"; + @NonNls public String questionString = + "add,are,can,check,contains,could,endsWith,equals,has,is,matches,must,put,remove,shall,should,startsWith,was,were,will,would"; @SuppressWarnings({"PublicField"}) public boolean ignoreBooleanMethods = false; @SuppressWarnings({"PublicField"}) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExceptionUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExceptionUtils.java index 39e4f70c7935..7b49568f45a0 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExceptionUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExceptionUtils.java @@ -260,7 +260,10 @@ public class ExceptionUtils { if (exception == null) { return; } - m_exceptionsThrown.add(exception.getType()); + final PsiType type = exception.getType(); + if (type != null) { + m_exceptionsThrown.add(type); + } } @Override @@ -283,7 +286,7 @@ public class ExceptionUtils { } } - private static boolean isExceptionHandled(Set exceptionsHandled, PsiType thrownType) { + private static boolean isExceptionHandled(Set exceptionsHandled, @NotNull PsiType thrownType) { if (exceptionsHandled.contains(thrownType)) { return true; } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/KeepResourceCallInBlock.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/KeepResourceCallInBlock.after.java new file mode 100644 index 000000000000..c834d6358166 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/KeepResourceCallInBlock.after.java @@ -0,0 +1,10 @@ +import java.io.Reader; + +class X { + public void test() throws Exception { + + try (Reader r = new FileReader("")) { + r.read(); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/KeepResourceCallInBlock.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/KeepResourceCallInBlock.java new file mode 100644 index 000000000000..440351deeddb --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/KeepResourceCallInBlock.java @@ -0,0 +1,14 @@ +import java.io.Reader; + +class X { + public void test() throws Exception { + Reader r = new FileReader(""); + + try { + r.read(); + } + finally { + r.close(); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/toobroadcatch/TooBroadCatchBlock.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/toobroadcatch/TooBroadCatchBlock.java index 19823d85d7c8..10e079c06d41 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/toobroadcatch/TooBroadCatchBlock.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/toobroadcatch/TooBroadCatchBlock.java @@ -114,4 +114,10 @@ public class TooBroadCatchBlock{ } } + + void incomplete() { + try { + throw undeclared; + } catch (NumberFormatException e) {} + } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/javabeans/suspicious_getter_setter/SuspiciousGetterSetter.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/javabeans/suspicious_getter_setter/SuspiciousGetterSetter.java index 1194eb44fda7..fa8af9982ab3 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/javabeans/suspicious_getter_setter/SuspiciousGetterSetter.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/javabeans/suspicious_getter_setter/SuspiciousGetterSetter.java @@ -3,6 +3,8 @@ public class SuspiciousGetterSetter { private String myOne; private String myTwo; + private static final String MSG_KEY = ""; + public String getTwo() { return myOne; } @@ -10,4 +12,8 @@ public class SuspiciousGetterSetter { public void setTwo(String two) { myOne = two; } + + String getMsgKey() { // suspicious getter + return MSG_KEY; + } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/naming/boolean_method_name_must_start_with_question/BooleanMethodNameMustStartWithQuestion.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/naming/boolean_method_name_must_start_with_question/BooleanMethodNameMustStartWithQuestion.java new file mode 100644 index 000000000000..926c83379900 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/naming/boolean_method_name_must_start_with_question/BooleanMethodNameMustStartWithQuestion.java @@ -0,0 +1,10 @@ +class BooleanMethodNameMustStartWithQuestion { + + public boolean areYouAllRight() { + return true; + } + + public boolean setup() { + return false; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/migration/TryFinallyCanBeTryWithResourcesFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/migration/TryFinallyCanBeTryWithResourcesFixTest.java index 766077afdebd..bf4852456402 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/migration/TryFinallyCanBeTryWithResourcesFixTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/migration/TryFinallyCanBeTryWithResourcesFixTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ public class TryFinallyCanBeTryWithResourcesFixTest extends IGQuickFixesTestCase public void testComments() { doTest(); } public void testMultiple() { doTest(); } public void testDependents() { doTest(); } + public void testKeepResourceCallInBlock() { doTest(); } @Override public void setUp() throws Exception { diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionTest.java new file mode 100644 index 000000000000..42681485f68f --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionTest.java @@ -0,0 +1,37 @@ +/* + * 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.siyeh.ig.naming; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import junit.framework.TestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class BooleanMethodNameMustStartWithQuestionInspectionTest extends LightInspectionTestCase { + + public void testBooleanMethodNameMustStartWithQuestion() { + doTest(); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new BooleanMethodNameMustStartWithQuestionInspection(); + } +} \ No newline at end of file diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/MethodParameterInjection.java b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/MethodParameterInjection.java index 464aa38ccc03..024f822c43ad 100644 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/MethodParameterInjection.java +++ b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/MethodParameterInjection.java @@ -16,7 +16,6 @@ package org.intellij.plugins.intelliLang.inject.config; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizableStringList; import com.intellij.openapi.util.JDOMExternalizer; import com.intellij.openapi.util.Pair; @@ -28,6 +27,7 @@ import com.intellij.util.IncorrectOperationException; import gnu.trove.THashMap; import org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport; import org.jdom.Element; +import org.jdom.IllegalDataException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -97,8 +97,7 @@ public class MethodParameterInjection extends BaseInjection { try { list.readExternal(e); } - catch (InvalidDataException e1) { - // nothing + catch (IllegalDataException ignored) { } if (list.isEmpty()) return; final boolean[] selection = new boolean[list.size()]; diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/trivialif/MergeElseIfIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/trivialif/MergeElseIfIntention.java index ab81f4cbdad9..7f4212d4d115 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/trivialif/MergeElseIfIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/trivialif/MergeElseIfIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2006 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2015 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,31 +16,73 @@ package com.siyeh.ipp.trivialif; import com.intellij.psi.*; -import com.intellij.util.IncorrectOperationException; import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; + public class MergeElseIfIntention extends Intention { + @Override @NotNull public PsiElementPredicate getElementPredicate() { return new MergeElseIfPredicate(); } - public void processIntention(PsiElement element) - throws IncorrectOperationException { + @Override + public void processIntention(PsiElement element) { final PsiJavaToken token = (PsiJavaToken)element; - final PsiIfStatement parentStatement = - (PsiIfStatement)token.getParent(); + final PsiIfStatement parentStatement = (PsiIfStatement)token.getParent(); assert parentStatement != null; - final PsiBlockStatement elseBranch = - (PsiBlockStatement)parentStatement.getElseBranch(); + final PsiBlockStatement elseBranch = (PsiBlockStatement)parentStatement.getElseBranch(); assert elseBranch != null; + final PsiElement lastChild = elseBranch.getLastChild(); final PsiCodeBlock elseBranchBlock = elseBranch.getCodeBlock(); - final PsiStatement elseBranchContents = - elseBranchBlock.getStatements()[0]; + final PsiStatement elseBranchContents = elseBranchBlock.getStatements()[0]; + final PsiElement grandParent = parentStatement.getParent(); + if (lastChild instanceof PsiComment) { + addElementAndPrevWhiteSpace(lastChild, grandParent, parentStatement); + } + final List before = new ArrayList(1); + final List after = new ArrayList(1); + collectComments(elseBranchContents, before, after); + for (PsiComment comment : before) { + addElementAndPrevWhiteSpace(comment, parentStatement, token); + } + for (PsiComment comment : after) { + grandParent.addAfter(comment, parentStatement); + } PsiReplacementUtil.replaceStatement(elseBranch, elseBranchContents.getText()); } + + private static void addElementAndPrevWhiteSpace(PsiElement element, PsiElement container, PsiElement anchor) { + final PsiElement sibling = element.getPrevSibling(); + container.addAfter(element, anchor); + if (sibling instanceof PsiWhiteSpace) { + container.addAfter(sibling, anchor); + } + } + + /** + * Before comments are added in reverse order of appearance in the code. + */ + private static void collectComments(PsiStatement statement, List before, List after) { + PsiElement prevSibling = statement.getPrevSibling(); + while (prevSibling != null) { + if (prevSibling instanceof PsiComment) { + before.add((PsiComment)prevSibling); + } + prevSibling = prevSibling.getPrevSibling(); + } + PsiElement nextSibing = statement.getNextSibling(); + while (nextSibing != null) { + if (nextSibing instanceof PsiComment) { + after.add((PsiComment)nextSibing); + } + nextSibing = nextSibing.getNextSibling(); + } + } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/trivialif/MergeElseIfIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/trivialif/MergeElseIfIntentionTest.java new file mode 100644 index 000000000000..fb4324e1e99e --- /dev/null +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/trivialif/MergeElseIfIntentionTest.java @@ -0,0 +1,63 @@ +/* + * 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. + */ + +/** + * (c) 2015 Silent Forest AB + * created: 27 August 2015 + */ +package com.siyeh.ipp.trivialif; + +import com.siyeh.ipp.IPPTestCase; + +/** + * @author Bas Leijdekkers + */ +public class MergeElseIfIntentionTest extends IPPTestCase { + + public void testCommentsAreKept() { + doTest("class X {\n" + + " void test(boolean foo, boolean bar) {\n" + + " if (foo) { //asdf\n" + + "\n" + + " } else/*_Merge 'else if'*/ {\n" + + " // blubb\n" + + " // blabb\n" + + " if (bar) { // asdf3\n" + + "\n" + + " } // other\n" + + " // bla\n" + + " } // asdf1\n" + + " // asdf4\n" + + " }\n" + + "}\n", + + "class X {\n" + + " void test(boolean foo, boolean bar) {\n" + + " if (foo) { //asdf\n" + + "\n" + + " } else\n" + + " // blubb\n" + + " // blabb\n" + + " if (bar) { // asdf3\n" + + "\n" + + " } // other\n" + + "// bla\n" + + "// asdf1\n" + + " // asdf4\n" + + " }\n" + + "}\n"); + } +} diff --git a/plugins/ant/src/com/intellij/lang/ant/config/execution/TreeView.java b/plugins/ant/src/com/intellij/lang/ant/config/execution/TreeView.java index eca201f5d679..2c9f1e15b49f 100644 --- a/plugins/ant/src/com/intellij/lang/ant/config/execution/TreeView.java +++ b/plugins/ant/src/com/intellij/lang/ant/config/execution/TreeView.java @@ -15,10 +15,10 @@ */ package com.intellij.lang.ant.config.execution; -import com.intellij.ide.CopyProvider; import com.intellij.ide.DataManager; import com.intellij.ide.OccurenceNavigator; import com.intellij.ide.OccurenceNavigatorSupport; +import com.intellij.ide.TextCopyProvider; import com.intellij.lang.ant.AntBundle; import com.intellij.lang.ant.config.AntBuildFile; import com.intellij.lang.ant.config.AntBuildModelBase; @@ -28,7 +28,6 @@ import com.intellij.lang.ant.config.impl.BuildTask; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; -import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; @@ -44,18 +43,14 @@ import com.intellij.util.StringBuilderSpinAllocator; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.*; import java.awt.*; -import java.awt.datatransfer.StringSelection; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.StringTokenizer; +import java.util.*; public final class TreeView implements AntOutputView, OccurenceNavigator { private Tree myTree; @@ -603,31 +598,17 @@ public final class TreeView implements AntOutputView, OccurenceNavigator { public Object getData(String dataId) { if (PlatformDataKeys.COPY_PROVIDER.is(dataId)) { - return new CopyProvider() { - public boolean isCopyEnabled(@NotNull DataContext dataContext) { - return getSelectionPath() != null; - } - - public boolean isCopyVisible(@NotNull DataContext dataContext) { - return true; - } - - public void performCopy(@NotNull DataContext dataContext) { + return new TextCopyProvider() { + @Nullable + @Override + public Collection getTextLinesToCopy() { TreePath selection = getSelectionPath(); - Object value = selection.getLastPathComponent(); - String text; + Object value = selection == null ? null : selection.getLastPathComponent(); if (value instanceof MessageNode) { MessageNode messageNode = ((MessageNode)value); - String[] lines = messageNode.getText(); - text = ""; - for (String line : lines) { - text += line + "\n"; - } + return Arrays.asList(messageNode.getText()); } - else { - text = value.toString(); - } - CopyPasteManager.getInstance().setContents(new StringSelection(text)); + return value == null ? null : Collections.singleton(value.toString()); } }; } diff --git a/plugins/devkit/src/references/IconsReferencesContributor.java b/plugins/devkit/src/references/IconsReferencesContributor.java index e60e22a91bd9..5f22a8d2172b 100644 --- a/plugins/devkit/src/references/IconsReferencesContributor.java +++ b/plugins/devkit/src/references/IconsReferencesContributor.java @@ -311,7 +311,7 @@ public class IconsReferencesContributor extends PsiReferenceContributor implemen model.setCaseSensitive(true); model.setFindAll(true); model.setWholeWordsOnly(true); - FindInProjectUtil.findUsages(model, FindInProjectUtil.getPsiDirectory(model, project), project, new Processor() { + FindInProjectUtil.findUsages(model, project, new Processor() { @Override public boolean process(final UsageInfo usage) { ApplicationManager.getApplication().runReadAction(new Runnable() { diff --git a/plugins/git4idea/src/git4idea/log/GitRefManager.java b/plugins/git4idea/src/git4idea/log/GitRefManager.java index 6d4894ad3eea..2aab1c8b2666 100644 --- a/plugins/git4idea/src/git4idea/log/GitRefManager.java +++ b/plugins/git4idea/src/git4idea/log/GitRefManager.java @@ -4,15 +4,11 @@ import com.intellij.dvcs.repo.RepositoryManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.JBColor; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; -import com.intellij.vcs.log.RefGroup; -import com.intellij.vcs.log.VcsLogRefManager; -import com.intellij.vcs.log.VcsRef; -import com.intellij.vcs.log.VcsRefType; +import com.intellij.vcs.log.*; import com.intellij.vcs.log.impl.SingletonRefGroup; import com.intellij.vcs.log.impl.VcsLogUtil; import git4idea.GitBranch; @@ -31,17 +27,11 @@ import java.util.List; * @author Kirill Likhodedov */ public class GitRefManager implements VcsLogRefManager { - - private static final Color HEAD_COLOR = new JBColor(new Color(0xf1ef9e), new Color(113, 111, 64)); - private static final Color LOCAL_BRANCH_COLOR = new JBColor(new Color(0x75eec7), new Color(0x0D6D4F)); - private static final Color REMOTE_BRANCH_COLOR = new JBColor(new Color(0xbcbcfc), new Color(0xbcbcfc).darker().darker()); - private static final Color TAG_COLOR = JBColor.WHITE; - - public static final VcsRefType HEAD = new SimpleRefType(true, HEAD_COLOR, "HEAD"); - public static final VcsRefType LOCAL_BRANCH = new SimpleRefType(true, LOCAL_BRANCH_COLOR, "LOCAL_BRANCH"); - public static final VcsRefType REMOTE_BRANCH = new SimpleRefType(true, REMOTE_BRANCH_COLOR, "REMOTE_BRANCH"); - public static final VcsRefType TAG = new SimpleRefType(false, TAG_COLOR, "TAG"); - public static final VcsRefType OTHER = new SimpleRefType(false, TAG_COLOR, "OTHER"); + public static final VcsRefType HEAD = new SimpleRefType(true, VcsLogStandardColors.Refs.TIP, "HEAD"); + public static final VcsRefType LOCAL_BRANCH = new SimpleRefType(true, VcsLogStandardColors.Refs.BRANCH, "LOCAL_BRANCH"); + public static final VcsRefType REMOTE_BRANCH = new SimpleRefType(true, VcsLogStandardColors.Refs.BRANCH_REF, "REMOTE_BRANCH"); + public static final VcsRefType TAG = new SimpleRefType(false, VcsLogStandardColors.Refs.TAG, "TAG"); + public static final VcsRefType OTHER = new SimpleRefType(false, VcsLogStandardColors.Refs.TAG, "OTHER"); private static final String MASTER = "master"; private static final String ORIGIN_MASTER = "origin/master"; @@ -262,7 +252,7 @@ public class GitRefManager implements VcsLogRefManager { @NotNull @Override public Color getBgColor() { - return HEAD_COLOR; + return VcsLogStandardColors.Refs.TIP; } } @@ -295,7 +285,7 @@ public class GitRefManager implements VcsLogRefManager { @NotNull @Override public Color getBgColor() { - return REMOTE_BRANCH_COLOR; + return VcsLogStandardColors.Refs.BRANCH_REF; } } diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java index 52f656205d6c..c6ebbf8d5f2d 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,11 @@ package git4idea.rebase; import com.intellij.icons.AllIcons; import com.intellij.ide.CopyProvider; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.ide.CopyPasteManager; +import com.intellij.ide.TextCopyProvider; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonShortcuts; +import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; @@ -47,9 +50,9 @@ import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; -import java.awt.datatransfer.StringSelection; import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -422,24 +425,18 @@ public class GitRebaseEditor extends DialogWrapper implements DataProvider { } } - private class MyCopyProvider implements CopyProvider { + private class MyCopyProvider extends TextCopyProvider { + @Nullable @Override - public void performCopy(@NotNull DataContext dataContext) { - List data = ContainerUtil.newArrayList(); - for (int row : myCommitsTable.getSelectedRows()) { - data.add(myTableModel.getStringToCopy(row)); + public Collection getTextLinesToCopy() { + if (myCommitsTable.getSelectedRowCount() > 0) { + List lines = ContainerUtil.newArrayList(); + for (int row : myCommitsTable.getSelectedRows()) { + lines.add(myTableModel.getStringToCopy(row)); + } + return lines; } - CopyPasteManager.getInstance().setContents(new StringSelection(StringUtil.join(data, "\n"))); - } - - @Override - public boolean isCopyEnabled(@NotNull DataContext dataContext) { - return myCommitsTable.getSelectedRowCount() > 0; - } - - @Override - public boolean isCopyVisible(@NotNull DataContext dataContext) { - return true; + return null; } } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/config/GroovyConfigUtils.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/config/GroovyConfigUtils.java index 4420833d0181..1eb0df7aa948 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/config/GroovyConfigUtils.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/config/GroovyConfigUtils.java @@ -41,9 +41,9 @@ import java.util.regex.Pattern; */ public abstract class GroovyConfigUtils extends AbstractConfigUtils { - @NonNls public static final Pattern GROOVY_ALL_JAR_PATTERN = Pattern.compile("groovy-all(-minimal)?(-(\\d+(\\.\\d+)*))?\\.jar"); + @NonNls public static final Pattern GROOVY_ALL_JAR_PATTERN = Pattern.compile("groovy-all(-minimal)?(-(\\d+(\\.\\d+)*))?(-indy)?\\.jar"); public static final int VERSION_GROUP_NUMBER = 3; // version will be in third group in GROOVY_ALL_JAR_PATTERN - @NonNls public static final Pattern GROOVY_JAR_PATTERN = Pattern.compile("groovy(-(\\d(\\.\\d)*))?\\.jar"); + @NonNls public static final Pattern GROOVY_JAR_PATTERN = Pattern.compile("groovy(-(\\d(\\.\\d)*))?(-indy)?\\.jar"); public static final String NO_VERSION = ""; public static final String GROOVY1_7 = "1.7"; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java index fd97d501e0b2..945cdb422106 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java @@ -21,7 +21,6 @@ import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.SimpleModificationTracker; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import com.intellij.psi.impl.DelegateSubstitutor; import com.intellij.psi.impl.compiled.ClsClassImpl; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.util.*; @@ -38,7 +37,10 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAc import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrReflectedMethod; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; -import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.*; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightField; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrTraitField; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrTraitMethod; import org.jetbrains.plugins.groovy.lang.psi.util.GrClassImplUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil; import org.jetbrains.plugins.groovy.lang.resolve.GroovyTraitFieldsFileIndex.TraitFieldDescriptor; @@ -123,7 +125,7 @@ public class GrTypeDefinitionMembersCache { final List result = ContainerUtil.newArrayList(); final GrTypeDefinitionBody body = myDefinition.getBody(); if (body != null) ContainerUtil.addAll(result, body.getInnerClasses()); - result.addAll(AstTransformContributor.runContributors(myDefinition).getClasses()); + result.addAll(AstTransformContributor.runContributorsForClasses(myDefinition)); return Result.create(result.toArray(new PsiClass[result.size()]), myTreeChangeTracker); } }); @@ -153,7 +155,7 @@ public class GrTypeDefinitionMembersCache { @Nullable @Override public Result> compute() { - return Result.create(AstTransformContributor.runContributors(myDefinition).getFields(), myTreeChangeTracker, + return Result.create(AstTransformContributor.runContributorsForFields(myDefinition), myTreeChangeTracker, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); } }); @@ -167,7 +169,7 @@ public class GrTypeDefinitionMembersCache { GrClassImplUtil.collectMethodsFromBody(myDefinition, result); result.addAll(new TraitCollector().collectMethods(result)); - for (PsiMethod method : AstTransformContributor.runContributors(myDefinition).getMethods()) { + for (PsiMethod method : AstTransformContributor.runContributorsForMethods(myDefinition)) { GrClassImplUtil.addExpandingReflectedMethods(result, method); } @@ -283,40 +285,17 @@ public class GrTypeDefinitionMembersCache { final PsiType classType = TypesUtil.createJavaLangClassType( psiFacade.getElementFactory().createType(trait), trait.getProject(), trait.getResolveScope() ); - final PsiSubstitutor delegateSubstitutor = getSubstitutor(substitutor, trait); for (PsiMethod method : traitHelper.getMethods()) { if (!method.hasModifierProperty(PsiModifier.STATIC)) continue; final PsiParameter[] parameters = method.getParameterList().getParameters(); if (parameters.length <= 0) continue; final PsiParameter self = parameters[0]; if (self.getType().equals(classType)) { - addCandidate(GrGdkMethodImpl.createGdkMethod(method, true, "via @Trait"), delegateSubstitutor); + addCandidate(GrTraitUtil.createTraitMethodFromCompiledHelperMethod(method, trait), substitutor); } } } } - - @NotNull - private DelegateSubstitutor getSubstitutor(@NotNull final PsiSubstitutor substitutor, - @NotNull final PsiClass trait) { - final Map substitutionMap = ContainerUtil.newTroveMap(); - for (PsiTypeParameter parameter : trait.getTypeParameters()) { - substitutionMap.put(parameter.getName(), substitutor.substitute(parameter)); - } - return new DelegateSubstitutor(substitutor) { - @Override - public PsiType substitute(@Nullable PsiType type) { - final PsiType substituted = super.substitute(type); - if (type != null && (substituted == null || substituted.equals(type))) { - final PsiType byName = substitutionMap.get(type.getCanonicalText()); - return byName == null ? substituted : byName; - } - else { - return substituted; - } - } - }; - } }.getResult(); for (CandidateInfo candidateInfo : concreteTraitMethods) { List methodsToAdd = getExpandingMethods(candidateInfo); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrClassImplUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrClassImplUtil.java index 1777eaf686c1..2805108b6496 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrClassImplUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrClassImplUtil.java @@ -141,7 +141,7 @@ public class GrClassImplUtil { final PsiClassType[] implementsTypes = getReferenceListTypes(grType.getImplementsClause()); final PsiClassType[] extendsTypes = getReferenceListTypes(grType.getExtendsClause()); result.addAll(Arrays.asList(implementsTypes)); - result.addAll(AstTransformContributor.runContributors(grType).getImplementsTypes()); + result.addAll(AstTransformContributor.runContributorsForImplementsTypes(grType)); if (!grType.isInterface() && !ContainerUtil.or(implementsTypes, IS_GROOVY_OBJECT) && !ContainerUtil.or(extendsTypes, IS_GROOVY_OBJECT)) { result.add(getGroovyObjectType(grType)); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrTraitUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrTraitUtil.java index bc5d3dfb64d2..8af949509fd6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrTraitUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GrTraitUtil.java @@ -17,13 +17,17 @@ package org.jetbrains.plugins.groovy.lang.psi.util; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiMethod; +import com.intellij.psi.*; import com.intellij.psi.impl.compiled.ClsClassImpl; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder; + +import java.util.Map; import static com.intellij.psi.PsiModifier.ABSTRACT; @@ -68,4 +72,71 @@ public class GrTraitUtil { && containingClass.isInterface() && AnnotationUtil.isAnnotated(containingClass, "groovy.transform.Trait", false); } + + public static PsiMethod createTraitMethodFromCompiledHelperMethod(final PsiMethod compiledMethod, PsiClass trait) { + assert compiledMethod.getParameterList().getParametersCount() > 0; + + final GrLightMethodBuilder result = new GrLightMethodBuilder(compiledMethod.getManager(), compiledMethod.getName()); + result.setNavigationElement(compiledMethod); + result.setOriginInfo("via @Trait"); + result.addModifier(PsiModifier.STATIC); + for (PsiTypeParameter parameter : compiledMethod.getTypeParameters()) { + result.getTypeParameterList().addParameter(parameter); + } + + final Map substitutionMap = ContainerUtil.newTroveMap(); + for (PsiTypeParameter parameter : trait.getTypeParameters()) { + substitutionMap.put(parameter.getName(), parameter); + } + + final PsiElementFactory myElementFactory = JavaPsiFacade.getInstance(compiledMethod.getProject()).getElementFactory(); + final PsiTypeVisitor corrector = new PsiTypeMapper() { + + @Nullable + @Override + public PsiType visitClassType(PsiClassType classType) { + final PsiClass resolved = classType.resolve(); + // if resolved to method parameter -> return as is + if (resolved instanceof PsiTypeParameter && compiledMethod.equals(((PsiTypeParameter)resolved).getOwner())) return classType; + if (resolved == null) { + // if not resolved -> try to get from map + final PsiTypeParameter byName = substitutionMap.get(classType.getCanonicalText()); + return byName == null ? classType : myElementFactory.createType(byName); + } + else { + // if resolved -> get from map anyways + final PsiTypeParameter byName = substitutionMap.get(resolved.getName()); + final PsiTypeVisitor $this = this; + final PsiType[] substitutes = !classType.hasParameters() ? PsiType.EMPTY_ARRAY : ContainerUtil.map2Array( + classType.getParameters(), PsiType.class, new Function() { + @Override + public PsiType fun(PsiType type) { + return type.accept($this); + } + } + ); + return myElementFactory.createType(byName != null ? byName : resolved, substitutes); + } + } + }; + + for (int i = 1; i < compiledMethod.getParameterList().getParameters().length; i++) { + final PsiParameter originalParameter = compiledMethod.getParameterList().getParameters()[i]; + final PsiType originalType = originalParameter.getType(); + final PsiType correctedType = trait.hasTypeParameters() ? originalType.accept(corrector) : originalType; + result.addParameter(originalParameter.getName(), correctedType, false); + } + + for (PsiClassType type : compiledMethod.getThrowsList().getReferencedTypes()) { + final PsiType correctedType = trait.hasTypeParameters() ? type.accept(corrector) : type; + result.getThrowsList().addReference(correctedType instanceof PsiClassType ? (PsiClassType)correctedType : type); + } + + { + final PsiType originalType = compiledMethod.getReturnType(); + result.setReturnType(originalType != null && trait.hasTypeParameters() ? originalType.accept(corrector) : originalType); + } + + return result; + } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AstTransformContributor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AstTransformContributor.java index 73d985a36920..0bc0435d202d 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AstTransformContributor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AstTransformContributor.java @@ -18,12 +18,17 @@ package org.jetbrains.plugins.groovy.lang.resolve.ast; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.RecursionManager; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiClassType; import com.intellij.psi.PsiMethod; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; /** * @author Max Medvedev @@ -32,40 +37,71 @@ public abstract class AstTransformContributor { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("org.intellij.groovy.astTransformContributor"); - @Deprecated public void collectMethods(@NotNull final GrTypeDefinition clazz, Collection collector) { - } - @Deprecated public void collectFields(@NotNull final GrTypeDefinition clazz, Collection collector) { - } - /** - * Subclasses should override this method. - */ - @NotNull - @SuppressWarnings("deprecation") - public Members collect(@NotNull final GrTypeDefinition clazz) { - final Members members = Members.create(); - collectMethods(clazz, members.getMethods()); - collectFields(clazz, members.getFields()); - return members; + public void collectClasses(@NotNull final GrTypeDefinition clazz, Collection collector) { } - @NotNull - public static Members runContributors(@NotNull final GrTypeDefinition clazz) { - Members result = RecursionManager.doPreventingRecursion(clazz, true, new Computable() { + public void collectImplementsTypes(GrTypeDefinition clazz, Collection collector) { + } + + public static Collection runContributorsForMethods(final GrTypeDefinition clazz) { + Collection result = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { @Override - public Members compute() { - Members members = Members.create(); + public Collection compute() { + final ArrayList result = ContainerUtil.newArrayList(); for (final AstTransformContributor contributor : EP_NAME.getExtensions()) { - members.addFrom(contributor.collect(clazz)); + contributor.collectMethods(clazz, result); } - return members; + return result; } }); - return result == null ? Members.EMPTY : result; + return result == null ? Collections.emptyList() : result; + } + + public static Collection runContributorsForFields(final GrTypeDefinition clazz) { + Collection result = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { + @Override + public Collection compute() { + final ArrayList result = ContainerUtil.newArrayList(); + for (final AstTransformContributor contributor : EP_NAME.getExtensions()) { + contributor.collectFields(clazz, result); + } + return result; + } + }); + return result == null ? Collections.emptyList() : result; + } + + public static Collection runContributorsForClasses(final GrTypeDefinition clazz) { + Collection result = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { + @Override + public Collection compute() { + final ArrayList result = ContainerUtil.newArrayList(); + for (final AstTransformContributor contributor : EP_NAME.getExtensions()) { + contributor.collectClasses(clazz, result); + } + return result; + } + }); + return result == null ? Collections.emptyList() : result; + } + + public static Collection runContributorsForImplementsTypes(final GrTypeDefinition clazz) { + Collection result = RecursionManager.doPreventingRecursion(clazz, true, new Computable>() { + @Override + public Collection compute() { + final ArrayList result = ContainerUtil.newArrayList(); + for (final AstTransformContributor contributor : EP_NAME.getExtensions()) { + contributor.collectImplementsTypes(clazz, result); + } + return result; + } + }); + return result == null ? Collections.emptyList() : result; } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/DelegatedMethodsContributor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/DelegatedMethodsContributor.java index 0fd1a84f65b1..4054456082e8 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/DelegatedMethodsContributor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/DelegatedMethodsContributor.java @@ -50,16 +50,8 @@ import java.util.*; */ public class DelegatedMethodsContributor extends AstTransformContributor { - @NotNull @Override - public Members collect(@NotNull GrTypeDefinition clazz) { - final Members result = Members.create(); - doCollectMethods(clazz, result.getMethods()); - doCollectImplementsTypes(clazz, result.getImplementsTypes()); - return result; - } - - private static void doCollectMethods(@NotNull final GrTypeDefinition clazz, @NotNull Collection collector) { + public void collectMethods(@NotNull final GrTypeDefinition clazz, @NotNull Collection collector) { Set processed = new HashSet(); if (!checkForDelegate(clazz)) return; @@ -344,7 +336,8 @@ public class DelegatedMethodsContributor extends AstTransformContributor { private static final Set OBJECT_METHODS = ContainerUtil.newHashSet("equals", "hashCode", "getClass", "clone", "toString", "notify", "notifyAll", "wait", "finalize"); private static final Set GROOVY_OBJECT_METHODS = ContainerUtil.newHashSet("invokeMethod", "getProperty", "setProperty", "getMetaClass", "setMetaClass"); - private static void doCollectImplementsTypes(GrTypeDefinition grType, Collection result) { + @Override + public void collectImplementsTypes(GrTypeDefinition grType, Collection result) { final GrField[] fields = grType.getCodeFields(); for (GrField field : fields) { final PsiAnnotation delegate = PsiImplUtil.getAnnotation(field, GroovyCommonClassNames.GROOVY_LANG_DELEGATE); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java index 76ac329629c5..0b1369c87a75 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/DefaultBuilderStrategySupport.java @@ -30,12 +30,23 @@ import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderAnnotationContributor; import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderHelperLightPsiClass; +import java.util.Collection; + public class DefaultBuilderStrategySupport extends BuilderAnnotationContributor { public static final String DEFAULT_STRATEGY_NAME = "DefaultStrategy"; - @NotNull @Override + public void collectClasses(@NotNull GrTypeDefinition clazz, Collection collector) { + collector.addAll(collect(clazz).getClasses()); + } + + @Override + public void collectMethods(@NotNull GrTypeDefinition clazz, Collection collector) { + collector.addAll(collect(clazz).getMethods()); + } + + @NotNull public Members collect(@NotNull final GrTypeDefinition typeDefinition) { return new DefaultBuilderStrategyHandler(typeDefinition).doProcess(); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/ExternalBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/ExternalBuilderStrategySupport.java index f001f78b8417..b1a094ace7e1 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/ExternalBuilderStrategySupport.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/ExternalBuilderStrategySupport.java @@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy; import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; @@ -25,6 +26,8 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderAnnotationContributor; +import java.util.Collection; + import static org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport.createBuildMethod; import static org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport.createFieldSetter; @@ -32,8 +35,12 @@ public class ExternalBuilderStrategySupport extends BuilderAnnotationContributor public static final String EXTERNAL_STRATEGY_NAME = "ExternalStrategy"; - @NotNull @Override + public void collectMethods(@NotNull GrTypeDefinition clazz, Collection collector) { + collector.addAll(collect(clazz).getMethods()); + } + + @NotNull public Members collect(@NotNull GrTypeDefinition builderClass) { Pair definitionPair = getConstructedClass(builderClass); if (definitionPair == null) return Members.EMPTY; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/InitializerBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/InitializerBuilderStrategySupport.java index 39651af4c165..de675370a364 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/InitializerBuilderStrategySupport.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/InitializerBuilderStrategySupport.java @@ -30,6 +30,8 @@ import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderAnnotationContributor; import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderHelperLightPsiClass; +import java.util.Collection; + import static org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport.getBuilderClassName; import static org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport.getFieldMethodName; @@ -39,8 +41,17 @@ public class InitializerBuilderStrategySupport extends BuilderAnnotationContribu public static final String SET_FQN = "groovy.transform.builder.InitializerStrategy.SET"; public static final String UNSET_FQN = "groovy.transform.builder.InitializerStrategy.UNSET"; - @NotNull @Override + public void collectClasses(@NotNull GrTypeDefinition clazz, Collection collector) { + collector.addAll(collect(clazz).getClasses()); + } + + @Override + public void collectMethods(@NotNull GrTypeDefinition clazz, Collection collector) { + collector.addAll(collect(clazz).getMethods()); + } + + @NotNull public Members collect(@NotNull GrTypeDefinition clazz) { return new InitializerBuilderStrategyHandler(clazz).doProcess(); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/SimpleBuilderStrategySupport.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/SimpleBuilderStrategySupport.java index 107fbf9d4dfb..8783c7a039f6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/SimpleBuilderStrategySupport.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/builder/strategy/SimpleBuilderStrategySupport.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.AnnotationUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifier; import com.intellij.psi.impl.light.LightMethodBuilder; import org.jetbrains.annotations.NotNull; @@ -29,12 +30,18 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.resolve.ast.Members; import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.BuilderAnnotationContributor; +import java.util.Collection; + public class SimpleBuilderStrategySupport extends BuilderAnnotationContributor { public static final String SIMPLE_STRATEGY_NAME = "SimpleStrategy"; - @NotNull @Override + public void collectMethods(@NotNull GrTypeDefinition clazz, Collection collector) { + collector.addAll(collect(clazz).getMethods()); + } + + @NotNull public Members collect(@NotNull GrTypeDefinition typeDefinition) { final PsiAnnotation annotation = PsiImplUtil.getAnnotation(typeDefinition, BUILDER_FQN); if (!isApplicable(annotation, SIMPLE_STRATEGY_NAME)) return Members.EMPTY; diff --git a/plugins/groovy/rt-constants/src/org/jetbrains/groovy/compiler/rt/ClassDependencyLoader.java b/plugins/groovy/rt-constants/src/org/jetbrains/groovy/compiler/rt/ClassDependencyLoader.java index bbe739a59f55..596736914ac3 100644 --- a/plugins/groovy/rt-constants/src/org/jetbrains/groovy/compiler/rt/ClassDependencyLoader.java +++ b/plugins/groovy/rt-constants/src/org/jetbrains/groovy/compiler/rt/ClassDependencyLoader.java @@ -99,11 +99,21 @@ public class ClassDependencyLoader { aPackage.getAnnotations(); } } - catch (LinkageError e) { - throw new ClassNotFoundException(name); + catch (Error e) { + myVisited.remove(aClass); + //noinspection InstanceofCatchParameter + if (e instanceof LinkageError) { + throw new ClassNotFoundException(name); + } + throw e; } - catch (TypeNotPresentException e) { - throw new ClassNotFoundException(name); + catch (RuntimeException e) { + myVisited.remove(aClass); + //noinspection InstanceofCatchParameter + if (e instanceof TypeNotPresentException) { + throw new ClassNotFoundException(name); + } + throw e; } } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java index 7bfc95d97102..db2212dd4682 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java @@ -98,7 +98,7 @@ public class GroovyLineMarkerProvider implements LineMarkerProvider { element == ((GrMethod)parent).getNameIdentifierGroovy() && hasSuperMethods((GrMethod)element.getParent())) { final Icon icon = AllIcons.Gutter.OverridingMethod; - final MarkerType type = GroovyMarkerTypes.OVERRIDING_METHOD; + final MarkerType type = GroovyMarkerTypes.GR_OVERRIDING_METHOD; return new LineMarkerInfo(element, element.getTextRange(), icon, Pass.UPDATE_ALL, type.getTooltip(), type.getNavigationHandler(), GutterIconRenderer.Alignment.LEFT); } @@ -256,7 +256,7 @@ public class GroovyLineMarkerProvider implements LineMarkerProvider { } final MarkerType type = element instanceof GrField ? GroovyMarkerTypes.OVERRIDEN_PROPERTY_TYPE - : GroovyMarkerTypes.OVERRIDEN_METHOD; + : GroovyMarkerTypes.GR_OVERRIDEN_METHOD; LineMarkerInfo info = new LineMarkerInfo(range, range.getTextRange(), icon, Pass.UPDATE_OVERRIDEN_MARKERS, type.getTooltip(), type.getNavigationHandler(), GutterIconRenderer.Alignment.RIGHT); result.add(info); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyMarkerTypes.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyMarkerTypes.java index 38fac33ae3a3..987ae3ac643e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyMarkerTypes.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyMarkerTypes.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,7 +60,7 @@ import java.util.*; * @author Max Medvedev */ public class GroovyMarkerTypes { - static final MarkerType OVERRIDING_PROPERTY_TYPE = new MarkerType(new Function() { + static final MarkerType OVERRIDING_PROPERTY_TYPE = new MarkerType("OVERRIDING_PROPERTY_TYPE",new Function() { @Nullable @Override public String fun(PsiElement psiElement) { @@ -116,7 +116,7 @@ public class GroovyMarkerTypes { new MethodCellRenderer(showMethodNames)); } }); - static final MarkerType OVERRIDEN_PROPERTY_TYPE = new MarkerType(new Function() { + static final MarkerType OVERRIDEN_PROPERTY_TYPE = new MarkerType("OVERRIDEN_PROPERTY_TYPE",new Function() { @Nullable @Override public String fun(PsiElement element) { @@ -183,7 +183,7 @@ public class GroovyMarkerTypes { } } ); - public static final MarkerType OVERRIDING_METHOD = new MarkerType(new NullableFunction() { + public static final MarkerType GR_OVERRIDING_METHOD = new MarkerType("GR_OVERRIDING_METHOD", new NullableFunction() { @Override public String fun(PsiElement element) { PsiElement parent = element.getParent(); @@ -223,7 +223,7 @@ public class GroovyMarkerTypes { } }); - public static final MarkerType OVERRIDEN_METHOD = new MarkerType(new NullableFunction() { + public static final MarkerType GR_OVERRIDEN_METHOD = new MarkerType("GR_OVERRIDEN_METHOD",new NullableFunction() { @Override public String fun(PsiElement element) { PsiElement parent = element.getParent(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java index cb272e202350..2de1abf9f2df 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java @@ -25,6 +25,7 @@ import com.intellij.psi.*; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; @@ -118,7 +119,7 @@ public class GroovyNoVariantsDelegator extends CompletionContributor { final CompletionResultSet qualifiedCollector = result.withPrefixMatcher(fullPrefix); InheritorsHolder inheritors = new InheritorsHolder(result); for (final LookupElement base : suggestQualifierItems(parameters, (GrReferenceElement)qualifier, inheritors)) { - final PsiType type = JavaCompletionUtil.getLookupElementType(base); + final PsiType type = getPsiType(base.getObject()); if (type != null && !PsiType.VOID.equals(type)) { GrReferenceElement ref = createMockReference(position, type, base); PsiElement refName = ref.getReferenceNameElement(); @@ -139,6 +140,24 @@ public class GroovyNoVariantsDelegator extends CompletionContributor { } } + @Nullable + private static PsiType getPsiType(final Object o) { + if (o instanceof ResolveResult) { + return getPsiType(((ResolveResult)o).getElement()); + } + if (o instanceof PsiVariable) { + return ((PsiVariable)o).getType(); + } + else if (o instanceof PsiMethod) { + return ((PsiMethod)o).getReturnType(); + } + else if (o instanceof PsiClass) { + final PsiClass psiClass = (PsiClass)o; + return JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass); + } + return null; + } + private static GrReferenceElement createMockReference(final PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem) { GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(place.getProject()); if (qualifierItem.getObject() instanceof PsiClass) { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy index a5ea26bcc2b7..9786b3dd48ba 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy @@ -32,6 +32,7 @@ import com.intellij.openapi.compiler.CompilerMessage import com.intellij.openapi.compiler.CompilerMessageCategory import com.intellij.openapi.compiler.options.ExcludeEntryDescription import com.intellij.openapi.compiler.options.ExcludesConfiguration +import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.module.Module import com.intellij.openapi.roots.ModuleRootModificationUtil import com.intellij.openapi.util.Key @@ -51,6 +52,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyFile public abstract class GroovyCompilerTest extends GroovyCompilerTestCase { @Override protected void setUp() { super.setUp(); + Logger.getInstance("#org.jetbrains.plugins.groovy.compiler.GroovyCompilerTest").info(testStartMessage) addGroovyLibrary(myModule); } @@ -225,6 +227,8 @@ public abstract class GroovyCompilerTest extends GroovyCompilerTestCase { super.runBare() } + String getTestStartMessage() { "Starting " + getClass().name + " " + getName() } + @Override void runTest() { try { @@ -236,14 +240,10 @@ public abstract class GroovyCompilerTest extends GroovyCompilerTestCase { } } - private static void printLogs() { - def ideaLog = new File(TestLoggerFactory.testLogDir, "idea.log") - if (ideaLog.exists()) { - println "\n\nIdea Log:" - def limit = 20000 - def logText = ideaLog.text - println(logText.size() < limit ? logText : logText.substring(logText.size() - limit)) - } + private void printLogs() { + println "Idea log" + TestLoggerFactory.dumpLogToStdout(getTestStartMessage()) + def makeLog = new File(TestLoggerFactory.testLogDir, "../log/build-log/build.log") if (makeLog.exists()) { println "\n\nServer Log:" @@ -884,11 +884,20 @@ class AppTest { } public void "test extend groovy classes with additional dependencies"() { + def anotherModule = addModule("another", true) + addGroovyLibrary(anotherModule) + PsiTestUtil.addLibrary(myModule, "junit", GroovyFacetUtil.libDirectory, "junit.jar"); - PsiTestUtil.addLibrary(myModule, "cli", FileUtil.toCanonicalPath(PluginPathManager.getPluginHomePath("groovy") + "/../../build/lib"), "commons-cli-1.2.jar"); + + def cliPath = FileUtil.toCanonicalPath(PluginPathManager.getPluginHomePath("groovy") + "/../../build/lib") + PsiTestUtil.addLibrary(myModule, "cli", cliPath, "commons-cli-1.2.jar"); + PsiTestUtil.addLibrary(anotherModule, "cli", cliPath, "commons-cli-1.2.jar"); myFixture.addFileToProject("a.groovy", "class Foo extends GroovyTestCase {}") myFixture.addFileToProject("b.groovy", "class Bar extends CliBuilder {}") + + myFixture.addFileToProject("another/b.groovy", "class AnotherBar extends CliBuilder {}") + assertEmpty(make()) } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveCompiledTraitTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveCompiledTraitTest.groovy index eb5592383405..be82ee7d173a 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveCompiledTraitTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveCompiledTraitTest.groovy @@ -18,6 +18,8 @@ package org.jetbrains.plugins.groovy.lang.resolve import com.intellij.openapi.module.Module import com.intellij.openapi.roots.ContentEntry import com.intellij.openapi.roots.ModifiableRootModel +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiMethod import com.intellij.psi.impl.compiled.ClsClassImpl import com.intellij.testFramework.LightProjectDescriptor import com.intellij.testFramework.PsiTestUtil @@ -25,6 +27,7 @@ import groovy.transform.CompileStatic import org.jetbrains.annotations.NotNull import org.jetbrains.plugins.groovy.GroovyFileType import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrTraitField import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrTraitMethod import org.jetbrains.plugins.groovy.util.TestUtils @@ -176,6 +179,113 @@ class ExternalConcrete implements somepackage.TT { }''' } + void 'test trait parameter not within its bounds'() { + testHighlighting '''\ +class ExternalConcrete2 implements somepackage.GenericTraitInteger> {} +''' + } + + void 'test generic trait method'() { + def definition = configureTraitInheritor() + def method = definition.findMethodsByName("methodWithTraitGenerics", false)[0] + assert method.typeParameters.length == 0 + assert method.returnType.canonicalText == "Pojo" + assert method.parameterList.parametersCount == 2 + assert method.parameterList.parameters[0].type.canonicalText == "java.lang.String" + assert method.parameterList.parameters[1].type.canonicalText == "PojoInheritor" + } + + void 'test generic trait method with type parameters'() { + configureTraitInheritor() + def reference = configureByText( + 'foo.groovy', + 'new ExternalConcrete().methodWithMethodGenerics(1, "2", null)', + GrReferenceExpression + ) + def resolved = reference.advancedResolve() + def method = resolved.element as PsiMethod + def substitutor = resolved.substitutor + assert method.typeParameterList.typeParameters.length == 1 + assert substitutor.substitute(method.returnType).canonicalText == "java.lang.Integer" + assert method.parameterList.parametersCount == 3 + assert substitutor.substitute(method.parameterList.parameters[0].type).canonicalText == "java.lang.Integer" + assert method.parameterList.parameters[1].type.canonicalText == "java.lang.String" + assert method.parameterList.parameters[2].type.canonicalText == "PojoInheritor" + } + + void 'test generic trait method type parameters clashing'() { + configureTraitInheritor() + def reference = configureByText( + 'foo.groovy', + 'new ExternalConcrete().methodWithMethodGenericsClashing(1,"", new PojoInheritor())', + GrReferenceExpression + ) + def resolved = reference.advancedResolve() + def method = resolved.element as PsiMethod + def substitutor = resolved.substitutor + assert method.typeParameterList.typeParameters.length == 1 + assert substitutor.substitute(method.returnType).canonicalText == "java.lang.Integer" + assert method.parameterList.parametersCount == 3 + assert substitutor.substitute(method.parameterList.parameters[0].type).canonicalText == "java.lang.Integer" + assert method.parameterList.parameters[1].type.canonicalText == "java.lang.String" + assert method.parameterList.parameters[2].type.canonicalText == "PojoInheritor" + } + + + void 'test generic trait static method'() { + def definition = configureTraitInheritor() + def method = definition.findMethodsByName("staticMethodWithTraitGenerics", false)[0] + assert method.typeParameters.length == 0 + assert method.returnType.canonicalText == "Pojo" + assert method.parameterList.parametersCount == 2 + assert method.parameterList.parameters[0].type.canonicalText == "java.lang.String" + assert method.parameterList.parameters[1].type.canonicalText == "PojoInheritor" + } + + void 'test generic trait static method with type parameters'() { + configureTraitInheritor() + def reference = configureByText( + 'foo.groovy', + 'new ExternalConcrete().staticMethodWithMethodGenerics(1, "2", null)', + GrReferenceExpression + ) + def resolved = reference.advancedResolve() + def method = resolved.element as PsiMethod + def substitutor = resolved.substitutor + assert method.typeParameterList.typeParameters.length == 1 + assert substitutor.substitute(method.returnType).canonicalText == "java.lang.Integer" + assert method.parameterList.parametersCount == 3 + assert substitutor.substitute(method.parameterList.parameters[0].type).canonicalText == "java.lang.Integer" + assert method.parameterList.parameters[1].type.canonicalText == "java.lang.String" + assert method.parameterList.parameters[2].type.canonicalText == "PojoInheritor" + } + + void 'test generic trait static method type parameters clashing'() { + configureTraitInheritor() + def reference = configureByText( + 'foo.groovy', + 'new ExternalConcrete().staticMethodWithMethodGenericsClashing(1,"", new PojoInheritor())', + GrReferenceExpression + ) + def resolved = reference.advancedResolve() + def method = resolved.element as PsiMethod + def substitutor = resolved.substitutor + assert method.typeParameterList.typeParameters.length == 1 + assert substitutor.substitute(method.returnType).canonicalText == "java.lang.Integer" + assert method.parameterList.parametersCount == 3 + assert substitutor.substitute(method.parameterList.parameters[0].type).canonicalText == "java.lang.Integer" + assert method.parameterList.parameters[1].type.canonicalText == "java.lang.String" + assert method.parameterList.parameters[2].type.canonicalText == "PojoInheritor" + } + + private PsiClass configureTraitInheritor() { + myFixture.addFileToProject "inheritors.groovy", '''\ +class PojoInheritor extends somepackage.Pojo {} +class ExternalConcrete implements somepackage.GenericTrait {} +''' + myFixture.findClass("ExternalConcrete") + } + private testHighlighting(String text) { myFixture.configureByText(GroovyFileType.GROOVY_FILE_TYPE, text) myFixture.testHighlighting(true, false, true) diff --git a/plugins/groovy/testdata/lib/some-library.jar b/plugins/groovy/testdata/lib/some-library.jar index 4cfdef710e5a..0a4471eefadf 100644 Binary files a/plugins/groovy/testdata/lib/some-library.jar and b/plugins/groovy/testdata/lib/some-library.jar differ diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgRefManager.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgRefManager.java index 08fc07260b18..fe3a08730058 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgRefManager.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgRefManager.java @@ -18,10 +18,7 @@ package org.zmlx.hg4idea.log; import com.intellij.ui.JBColor; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import com.intellij.vcs.log.RefGroup; -import com.intellij.vcs.log.VcsLogRefManager; -import com.intellij.vcs.log.VcsRef; -import com.intellij.vcs.log.VcsRefType; +import com.intellij.vcs.log.*; import com.intellij.vcs.log.impl.SingletonRefGroup; import com.intellij.vcs.log.impl.VcsLogUtil; import org.jetbrains.annotations.NotNull; @@ -33,25 +30,19 @@ import java.util.Comparator; import java.util.List; public class HgRefManager implements VcsLogRefManager { - - private static final Color TIP_COLOR = new JBColor(new Color(0xf1ef9e), new Color(113, 111, 64)); - private static final Color HEAD_COLOR = new JBColor(new Color(0xF10FA9), new Color(0xF10FA9).darker()); - private static final Color BRANCH_COLOR = new JBColor(new Color(0x75eec7), new Color(0x0D6D4F)); private static final Color CLOSED_BRANCH_COLOR = new JBColor(new Color(0xee7f8a), new Color(0xee7f8a).darker()); - private static final Color BOOKMARK_COLOR = new JBColor(new Color(0xbcbcfc), new Color(0xbcbcfc).darker().darker()); - private static final Color TAG_COLOR = JBColor.WHITE; private static final Color LOCAL_TAG_COLOR = JBColor.CYAN; private static final Color MQ_TAG_COLOR = new JBColor(new Color(0x1764ff), new Color(0x1764ff).darker()); - public static final VcsRefType TIP = new SimpleRefType(true, TIP_COLOR); - public static final VcsRefType HEAD = new SimpleRefType(true, HEAD_COLOR); - public static final VcsRefType BRANCH = new SimpleRefType(true, BRANCH_COLOR); + public static final VcsRefType TIP = new SimpleRefType(true, VcsLogStandardColors.Refs.TIP); + public static final VcsRefType HEAD = new SimpleRefType(true, VcsLogStandardColors.Refs.LEAF); + public static final VcsRefType BRANCH = new SimpleRefType(true, VcsLogStandardColors.Refs.BRANCH); public static final VcsRefType CLOSED_BRANCH = new SimpleRefType(false, CLOSED_BRANCH_COLOR); - public static final VcsRefType BOOKMARK = new SimpleRefType(true, BOOKMARK_COLOR); - public static final VcsRefType TAG = new SimpleRefType(false, TAG_COLOR); + public static final VcsRefType BOOKMARK = new SimpleRefType(true, VcsLogStandardColors.Refs.BRANCH_REF); + public static final VcsRefType TAG = new SimpleRefType(false, VcsLogStandardColors.Refs.TAG); public static final VcsRefType LOCAL_TAG = new SimpleRefType(false, LOCAL_TAG_COLOR); public static final VcsRefType MQ_APPLIED_TAG = new SimpleRefType(false, MQ_TAG_COLOR); - + // first has the highest priority private static final List REF_TYPE_PRIORITIES = Arrays.asList(TIP, HEAD, BRANCH, BOOKMARK, TAG); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotationProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotationProvider.java index 451e113618ee..110d67a2b1f0 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotationProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotationProvider.java @@ -36,8 +36,6 @@ import java.util.List; public class HgAnnotationProvider implements AnnotationProvider { - private static final int DEFAULT_LIMIT = 500; - @NotNull private final Project myProject; public HgAnnotationProvider(@NotNull Project project) { @@ -63,7 +61,7 @@ public class HgAnnotationProvider implements AnnotationProvider { try { HgLogCommand logCommand = new HgLogCommand(myProject); logCommand.setFollowCopies(true); - logResult = logCommand.execute(fileToAnnotate, DEFAULT_LIMIT, false); + logResult = logCommand.execute(fileToAnnotate, -1, false); } catch (HgCommandException e) { throw new VcsException("Can not annotate, " + HgVcsMessages.message("hg4idea.error.log.command.execution"), e); diff --git a/plugins/junit/src/com/intellij/execution/junit/JUnitDependencyScopeSuggester.java b/plugins/junit/src/com/intellij/execution/junit/JUnitDependencyScopeSuggester.java index addb0b6fbc5e..f398e5183a42 100644 --- a/plugins/junit/src/com/intellij/execution/junit/JUnitDependencyScopeSuggester.java +++ b/plugins/junit/src/com/intellij/execution/junit/JUnitDependencyScopeSuggester.java @@ -24,18 +24,35 @@ import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; + /** * @author nik */ public class JUnitDependencyScopeSuggester extends LibraryDependencyScopeSuggester { + private static final String[] JUNIT_JAR_MARKERS = { + "org.junit.Test", "junit.framework.TestCase", "org.hamcrest.Matcher", "org.hamcrest.Matchers" + }; + @Nullable @Override public DependencyScope getDefaultDependencyScope(@NotNull Library library) { VirtualFile[] files = library.getFiles(OrderRootType.CLASSES); - if (files.length == 1 && (LibraryUtil.isClassAvailableInLibrary(files, "junit.framework.TestCase") - || LibraryUtil.isClassAvailableInLibrary(library, "org.junit.Test"))) { - return DependencyScope.TEST; + if (files.length == 0) return null; + for (VirtualFile file : files) { + if (!isTestJarRoot(file)) { + return null; + } } - return null; + return DependencyScope.TEST; + } + + private static boolean isTestJarRoot(VirtualFile file) { + for (String marker : JUNIT_JAR_MARKERS) { + if (LibraryUtil.isClassAvailableInLibrary(Collections.singletonList(file), marker)) { + return true; + } + } + return false; } } diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitDependencyScopeSuggesterTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitDependencyScopeSuggesterTest.java new file mode 100644 index 000000000000..9bcd6938db7c --- /dev/null +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitDependencyScopeSuggesterTest.java @@ -0,0 +1,63 @@ +/* + * 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.execution.junit; + +import com.intellij.openapi.projectRoots.ex.JavaSdkUtil; +import com.intellij.openapi.roots.DependencyScope; +import com.intellij.openapi.roots.LibraryOrderEntry; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.impl.OrderEntryUtil; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; +import com.intellij.util.ArrayUtil; + +/** + * @author nik + */ +public class JUnitDependencyScopeSuggesterTest extends JavaCodeInsightFixtureTestCase { + @Override + protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) throws Exception { + moduleBuilder.addLibrary("junit3", JavaSdkUtil.getJunit3JarPath()); + moduleBuilder.addLibrary("junit4", ArrayUtil.toStringArray(JavaSdkUtil.getJUnit4JarPaths())); + moduleBuilder.addLibrary("ideaRt", JavaSdkUtil.getIdeaRtJarPath()); + moduleBuilder.addLibrary("empty"); + } + + public void testJunit3() throws Exception { + assertSame(DependencyScope.TEST, getScope("junit3")); + } + + public void testJunit4() throws Exception { + assertSame(DependencyScope.TEST, getScope("junit4")); + } + + public void testOrdinaryLibrary() throws Exception { + assertNull(getScope("ideaRt")); + } + + public void testEmptyLibrary() throws Exception { + assertNull(getScope("empty")); + } + + private DependencyScope getScope(String name) { + LibraryOrderEntry entry = OrderEntryUtil.findLibraryOrderEntry(ModuleRootManager.getInstance(myModule), name); + assertNotNull(entry); + Library library = entry.getLibrary(); + assertNotNull(library); + return new JUnitDependencyScopeSuggester().getDefaultDependencyScope(library); + } +} \ No newline at end of file diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryAttachHandler.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryAttachHandler.java index 84907efe541a..7b0cbf7612cb 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryAttachHandler.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryAttachHandler.java @@ -381,7 +381,7 @@ public class RepositoryAttachHandler { List mavenIds, List extraTypes, Collection repositories, - final Processor> resultProcessor, + @Nullable final Processor> resultProcessor, ProgressIndicator indicator) { boolean cancelled = false; final Collection result = new LinkedHashSet(); @@ -434,7 +434,9 @@ public class RepositoryAttachHandler { DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() { @Override public void run() { - resultProcessor.process(new ArrayList(result)); + if (resultProcessor != null) { + resultProcessor.process(new ArrayList(result)); + } } }); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryLibraryType.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryLibraryType.java index 7b804b9a18e4..9a933db47799 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryLibraryType.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryLibraryType.java @@ -23,6 +23,7 @@ import com.intellij.openapi.roots.libraries.PersistentLibraryKind; import com.intellij.openapi.roots.libraries.ui.LibraryEditorComponent; import com.intellij.openapi.roots.libraries.ui.LibraryPropertiesEditor; import com.intellij.openapi.vfs.VirtualFile; +import icons.MavenIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -65,14 +66,13 @@ public class RepositoryLibraryType extends LibraryType component) { return new RepositoryLibraryWithDescriptionEditor(component); - // return new RepositoryLibraryEditor(component, this); } @Nullable @Override public Icon getIcon(@Nullable LibraryProperties properties) { if (properties == null || !(properties instanceof RepositoryLibraryProperties)) { - return getIcon(); + return MavenIcons.MavenLogo; } return RepositoryLibraryDescription.findDescription((RepositoryLibraryProperties)properties).getIcon(); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryUtils.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryUtils.java index e2372f199b41..926928f45b1f 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryUtils.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/library/RepositoryUtils.java @@ -53,6 +53,18 @@ public class RepositoryUtils { @NotNull static public final String ReleaseVersionId = "RELEASE"; @NotNull static public final String SnapshotVersionSuffix = "-SNAPSHOT"; + public static void downloadAsync(final Project project, + final boolean downloadSources, + final boolean downloadJavaDocs, + @NotNull final RepositoryLibraryProperties properties) { + String displayName = RepositoryLibraryDescription.findDescription(properties).getDisplayName(); + Task task = new Task.Backgroundable(project, ProjectBundle.message("maven.loading.library.hint", displayName), false) { + public void run(@NotNull ProgressIndicator indicator) { + List download = download(project, downloadSources, downloadJavaDocs, properties); + } + }; + ProgressManager.getInstance().run(task); + } public static List download(Project project, boolean downloadSources, boolean downloadJavaDocs, @@ -162,14 +174,14 @@ public class RepositoryUtils { return result; } - public static void loadDependencies(@NotNull ProgressIndicator indicator, - @NotNull final Project project, - @NotNull final LibraryEx library, - boolean downloadSources, - boolean downloadJavaDocs) { - indicator.setText(ProjectBundle.message("maven.loading.library.hint", library.getName())); - @NotNull final RepositoryLibraryProperties libraryProperties = (RepositoryLibraryProperties)library.getProperties(); - final String storageRoot = getStorageRoot(library, project); + public static synchronized void loadDependencies(@NotNull ProgressIndicator indicator, + @NotNull final Project project, + RepositoryLibraryProperties libraryProperties, + boolean downloadSources, + boolean downloadJavaDocs, + String displayName, + Processor> processor) { + indicator.setText(ProjectBundle.message("maven.loading.library.hint", displayName)); RepositoryAttachHandler.doResolveInner( project, @@ -178,39 +190,56 @@ public class RepositoryUtils { resolveEffectiveVersion(project, libraryProperties))), createExtraArtifactTypeList(downloadSources, downloadJavaDocs), RepositoryLibraryDescription.findDescription(libraryProperties).getRemoteRepositories(), - new Processor>() { - @Override - public boolean process(List artifacts) { - if (artifacts == null || artifacts.isEmpty()) { - return true; - } - final List roots = RepositoryAttachHandler.createRoots(artifacts, storageRoot); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - if (library.isDisposed()) { - return; - } - AccessToken token = WriteAction.start(); - try { - final NewLibraryEditor editor = new NewLibraryEditor(null, libraryProperties); - editor.removeAllRoots(); - editor.addRoots(roots); - final Library.ModifiableModel model = library.getModifiableModel(); - editor.applyTo((LibraryEx.ModifiableModelEx)model); - model.commit(); - } - finally { - token.finish(); - } - } - }); - return true; - } - }, + processor, indicator); } + + public static void loadDependencies(@NotNull ProgressIndicator indicator, + @NotNull final Project project, + @NotNull final LibraryEx library, + boolean downloadSources, + boolean downloadJavaDocs) { + final String storageRoot = getStorageRoot(library, project); + final RepositoryLibraryProperties libraryProperties = (RepositoryLibraryProperties)library.getProperties(); + loadDependencies(indicator, + project, + libraryProperties, + downloadSources, + downloadJavaDocs, + library.getName(), + new Processor>() { + @Override + public boolean process(List artifacts) { + if (artifacts == null || artifacts.isEmpty()) { + return true; + } + final List roots = RepositoryAttachHandler.createRoots(artifacts, storageRoot); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (library.isDisposed()) { + return; + } + AccessToken token = WriteAction.start(); + try { + final NewLibraryEditor editor = new NewLibraryEditor(null, libraryProperties); + editor.removeAllRoots(); + editor.addRoots(roots); + final Library.ModifiableModel model = library.getModifiableModel(); + editor.applyTo((LibraryEx.ModifiableModelEx)model); + model.commit(); + } + finally { + token.finish(); + } + } + }); + return true; + } + }); + } + public static void reloadDependencies(@NotNull ProgressIndicator indicator, @NotNull final Project project, @NotNull final LibraryEx library) { diff --git a/plugins/settings-repository/src/IcsManager.kt b/plugins/settings-repository/src/IcsManager.kt index 5f4cfa9c4663..db9948761494 100644 --- a/plugins/settings-repository/src/IcsManager.kt +++ b/plugins/settings-repository/src/IcsManager.kt @@ -17,13 +17,13 @@ package org.jetbrains.settingsRepository import com.intellij.configurationStore.StateStorageManagerImpl import com.intellij.configurationStore.StreamProvider +import com.intellij.configurationStore.isProjectOrModuleFile import com.intellij.ide.ApplicationLoadListener import com.intellij.openapi.application.Application import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.PathManager import com.intellij.openapi.components.RoamingType import com.intellij.openapi.components.StoragePathMacros -import com.intellij.openapi.components.impl.stores.StorageUtil import com.intellij.openapi.components.stateStore import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.progress.ProgressIndicator @@ -141,10 +141,10 @@ class IcsManager(dir: File) { } private inner class ProjectLevelProvider(projectId: String) : IcsStreamProvider(projectId) { - override fun isAutoCommit(fileSpec: String, roamingType: RoamingType) = !StorageUtil.isProjectOrModuleFile(fileSpec) + override fun isAutoCommit(fileSpec: String, roamingType: RoamingType) = !isProjectOrModuleFile(fileSpec) override fun isApplicable(fileSpec: String, roamingType: RoamingType): Boolean { - if (StorageUtil.isProjectOrModuleFile(fileSpec)) { + if (isProjectOrModuleFile(fileSpec)) { // applicable only if file was committed to Settings Server explicitly return repositoryManager.has(buildPath(fileSpec, roamingType, this.projectId)) } diff --git a/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/CourseProgress@2x.png b/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/CourseProgress@2x.png index 4b9fae90b122..5dad0c05270c 100644 Binary files a/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/CourseProgress@2x.png and b/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/CourseProgress@2x.png differ diff --git a/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/CourseProgress@2x_dark.png b/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/CourseProgress@2x_dark.png index f147a4f649ff..43efdc29cc1f 100644 Binary files a/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/CourseProgress@2x_dark.png and b/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/CourseProgress@2x_dark.png differ diff --git a/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/TaskDescription.png b/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/TaskDescription.png index 45b8651f497b..ec07f2c34289 100644 Binary files a/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/TaskDescription.png and b/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/TaskDescription.png differ diff --git a/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/TaskDescription_dark.png b/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/TaskDescription_dark.png index 345e8c90a4cc..4bff140d3e41 100644 Binary files a/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/TaskDescription_dark.png and b/python/educational/interactive-learning/resources/icons/com/jetbrains/edu/learning/TaskDescription_dark.png differ diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java index 25111e7b52c5..54ede8fc64c7 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java @@ -100,7 +100,17 @@ abstract public class StudyTaskNavigationAction extends DumbAwareAction { } } } - return shouldBeActive; + return shouldBeActive != null ? shouldBeActive : getFirstTaskFile(taskDir, project); + } + + @Nullable + private static VirtualFile getFirstTaskFile(@NotNull final VirtualFile taskDir, @NotNull final Project project) { + for (VirtualFile virtualFile : taskDir.getChildren()) { + if (StudyUtils.getTaskFile(project, virtualFile) != null) { + return virtualFile; + } + } + return null; } @Override diff --git a/python/educational/src/com/jetbrains/edu/EduAnswerPlaceholderPainter.java b/python/educational/src/com/jetbrains/edu/EduAnswerPlaceholderPainter.java index 0cbf0aae1e85..90b8078b3ba3 100644 --- a/python/educational/src/com/jetbrains/edu/EduAnswerPlaceholderPainter.java +++ b/python/educational/src/com/jetbrains/edu/EduAnswerPlaceholderPainter.java @@ -17,6 +17,10 @@ import java.awt.*; import java.util.List; public class EduAnswerPlaceholderPainter { + + //it should be the lowest highlighting layer, otherwise selection and other effects are not visible + public static final int PLACEHOLDERS_LAYER = 0; + private EduAnswerPlaceholderPainter() { } @@ -38,7 +42,7 @@ public class EduAnswerPlaceholderPainter { int highlighterLength = useLength ? length : replacementLength; final int endOffset = startOffset + highlighterLength; RangeHighlighter - highlighter = editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, HighlighterLayer.LAST + 1, + highlighter = editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, PLACEHOLDERS_LAYER, textAttributes, HighlighterTargetArea.EXACT_RANGE); highlighter.setCustomRenderer(new CustomHighlighterRenderer() { @Override diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index c149a45f197f..7dbb6d8cde9f 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -3,6 +3,7 @@ from __future__ import nested_scopes # Jython 2.1 support import pydev_monkey_qt from pydevd_utils import save_main_module +import pydevd_utils pydev_monkey_qt.patch_qt() @@ -28,7 +29,7 @@ from pydevd_comm import CMD_CHANGE_VARIABLE, \ CMD_STEP_OVER, \ CMD_STEP_RETURN, \ CMD_STEP_INTO_MY_CODE, \ - CMD_THREAD_KILL, \ + CMD_THREAD_KILL, \ CMD_THREAD_RUN, \ CMD_THREAD_SUSPEND, \ CMD_RUN_TO_LINE, \ @@ -51,7 +52,7 @@ from pydevd_comm import CMD_CHANGE_VARIABLE, \ InternalTerminateThread, \ InternalRunThread, \ InternalStepThread, \ - NetCommandFactory, \ + NetCommandFactory, \ PyDBDaemonThread, \ _queue, \ ReaderThread, \ @@ -63,15 +64,18 @@ from pydevd_comm import CMD_CHANGE_VARIABLE, \ StartServer, \ InternalSetNextStatementThread, \ ReloadCodeCommand, \ - CMD_SET_PY_EXCEPTION, \ + CMD_SET_PY_EXCEPTION, \ CMD_IGNORE_THROWN_EXCEPTION_AT,\ InternalGetBreakpointException, \ InternalSendCurrExceptionTrace,\ InternalSendCurrExceptionTraceProceeded,\ CMD_ENABLE_DONT_TRACE, \ CMD_GET_FILE_CONTENTS,\ - CMD_SET_PROPERTY_TRACE, CMD_RUN_CUSTOM_OPERATION,\ - InternalRunCustomOperation, CMD_EVALUATE_CONSOLE_EXPRESSION, InternalEvaluateConsoleExpression,\ + CMD_SET_PROPERTY_TRACE, \ + CMD_RUN_CUSTOM_OPERATION,\ + InternalRunCustomOperation, \ + CMD_EVALUATE_CONSOLE_EXPRESSION, \ + InternalEvaluateConsoleExpression,\ InternalConsoleGetCompletions from pydevd_file_utils import NormFileToServer, GetFilenameAndBase @@ -194,14 +198,14 @@ if hasattr(_temp, '_is_stopped'): # Python 3.4 has this return not t._is_stopped except: return t.isAlive() - + elif hasattr(_temp, '_Thread__stopped'): # Python 2.7 has this def isThreadAlive(t): try: return not t._Thread__stopped except: return t.isAlive() - + else: # Haven't checked all other versions, so, let's use the regular isAlive call in this case. def isThreadAlive(t): return t.isAlive() @@ -378,26 +382,16 @@ class PyDB: self.mpl_in_use = False self.mpl_hooks_in_debug_console = False self.mpl_modules_for_patching = {} + + self._filename_to_not_in_scope = {} def get_plugin_lazy_init(self): if self.plugin is None and SUPPORT_PLUGINS: self.plugin = PluginManager(self) return self.plugin - def get_project_roots(self): - if self.project_roots is None: - roots = os.getenv('IDE_PROJECT_ROOTS', '').split(os.pathsep) - pydev_log.debug("IDE_PROJECT_ROOTS %s\n" % roots) - self.project_roots = roots - def not_in_scope(self, filename): - self.get_project_roots() - filename = os.path.normcase(filename) - for root in self.project_roots: - root = os.path.normcase(root) - if filename.startswith(root): - return False - return True + return pydevd_utils.is_in_project_roots(filename) def first_appearance_in_scope(self, trace): if trace is None or self.not_in_scope(trace.tb_frame.f_code.co_filename): @@ -1217,10 +1211,10 @@ class PyDB: update_exception_hook(self) else: supported_type = False - + # I.e.: no need to initialize lazy (if we didn't have it in the first place, we can't remove # anything from it anyways). - plugin = self.plugin + plugin = self.plugin if plugin is not None: supported_type = plugin.remove_exception_breakpoint(self, type, exception) @@ -2078,15 +2072,15 @@ def stoptrace(): from pydev_monkey import undo_patch_thread_modules undo_patch_thread_modules() - + debugger = GetGlobalDebugger() - + if debugger: - + debugger.SetTraceForFrameAndParents( GetFrame(), also_add_to_passed_frame=True, overwrite_prev_trace=True, dispatch_func=lambda *args:None) debugger.exiting() - + killAllPydevThreads() connected = False @@ -2118,7 +2112,7 @@ class DispatchReader(ReaderThread): dummy_thread = threading.currentThread() dummy_thread.is_pydev_daemon_thread = False return ReaderThread.OnRun(self) - + def handleExcept(self): ReaderThread.handleExcept(self) @@ -2182,7 +2176,7 @@ class SetupHolder: # main #======================================================================================================================= if __name__ == '__main__': - + # parse the command line. --file is our last argument that is required try: sys.original_argv = sys.argv[:] diff --git a/python/helpers/pydev/pydevd_breakpoints.py b/python/helpers/pydev/pydevd_breakpoints.py index fdd069abcd1e..c349ccd1a70b 100644 --- a/python/helpers/pydev/pydevd_breakpoints.py +++ b/python/helpers/pydev/pydevd_breakpoints.py @@ -96,19 +96,19 @@ def _excepthook(exctype, value, tb): frames = [] debugger = GetGlobalDebugger() - user_frames = [] + user_frame = None while tb: frame = tb.tb_frame if exception_breakpoint.ignore_libraries and not debugger.not_in_scope(frame.f_code.co_filename): - user_frames.append(tb.tb_frame) + user_frame = tb.tb_frame frames.append(tb.tb_frame) tb = tb.tb_next thread = threadingCurrentThread() frames_byid = dict([(id(frame),frame) for frame in frames]) - if exception_breakpoint.ignore_libraries: - frame = user_frames[-1] + if exception_breakpoint.ignore_libraries and user_frame is not None: + frame = user_frame else: frame = frames[-1] thread.additionalInfo.exception = (exctype, value, tb) diff --git a/python/helpers/pydev/pydevd_signature.py b/python/helpers/pydev/pydevd_signature.py index 5967ab31786e..a37901214f23 100644 --- a/python/helpers/pydev/pydevd_signature.py +++ b/python/helpers/pydev/pydevd_signature.py @@ -31,17 +31,9 @@ class Signature(object): class SignatureFactory(object): def __init__(self): self._caller_cache = {} - self.project_roots = os.getenv('IDE_PROJECT_ROOTS', '').split(os.pathsep) def is_in_scope(self, filename): - filename = os.path.normcase(filename) - for root in self.project_roots: - root = os.path.normcase(root) - if filename.startswith(root): - return True - return False - - + return pydevd_utils.is_in_project_roots(filename) def create_signature(self, frame): try: diff --git a/python/helpers/pydev/pydevd_utils.py b/python/helpers/pydev/pydevd_utils.py index 054dd69327d5..34fc24cf37b0 100644 --- a/python/helpers/pydev/pydevd_utils.py +++ b/python/helpers/pydev/pydevd_utils.py @@ -5,9 +5,10 @@ try: except: from urllib.parse import quote +import os import pydevd_constants import sys - +import pydev_log def save_main_module(file, module_name): # patch provided by: Scott Schlesier - when script is run, it does not @@ -113,7 +114,35 @@ def quote_smart(s, safe='/'): s = s.encode('utf-8') return quote(s, safe) - - + + +def _get_project_roots(project_roots_cache=[]): + # Note: the project_roots_cache is the same instance among the many calls to the method + if not project_roots_cache: + roots = os.getenv('IDE_PROJECT_ROOTS', '').split(os.pathsep) + pydev_log.debug("IDE_PROJECT_ROOTS %s\n" % roots) + new_roots = [] + for root in roots: + new_roots.append(os.path.normcase(root)) + project_roots_cache.append(new_roots) + return project_roots_cache[-1] # returns the project roots with case normalized + + +def is_in_project_roots(filename, filename_to_not_in_scope_cache={}): + # Note: the filename_to_not_in_scope_cache is the same instance among the many calls to the method + try: + return filename_to_not_in_scope_cache[filename] + except: + project_roots = _get_project_roots() + filename = os.path.normcase(filename) + for root in project_roots: + if filename.startswith(root): + filename_to_not_in_scope_cache[filename] = False + break + else: # for else (only called if the break wasn't reached). + filename_to_not_in_scope_cache[filename] = True + + # at this point it must be loaded. + return filename_to_not_in_scope_cache[filename] diff --git a/python/src/com/jetbrains/python/refactoring/invertBoolean/PyInvertBooleanDelegate.java b/python/src/com/jetbrains/python/refactoring/invertBoolean/PyInvertBooleanDelegate.java index 3222cb567564..4e79f9f2688a 100644 --- a/python/src/com/jetbrains/python/refactoring/invertBoolean/PyInvertBooleanDelegate.java +++ b/python/src/com/jetbrains/python/refactoring/invertBoolean/PyInvertBooleanDelegate.java @@ -86,9 +86,9 @@ public class PyInvertBooleanDelegate extends InvertBooleanDelegate { @Override public void collectRefElements(PsiElement psiElement, - Collection elementsToInvert, @Nullable RenameProcessor renameProcessor, - @NotNull String newName) { + @NotNull String newName, + Collection elementsToInvert) { final Collection refs = ReferencesSearch.search(psiElement).findAll(); for (PsiReference ref : refs) { @@ -101,13 +101,6 @@ public class PyInvertBooleanDelegate extends InvertBooleanDelegate { ContainerUtil.addIfNotNull(elementsToInvert, getForeignElementToInvert(psiElement, refElement, PythonLanguage.getInstance())); } } - - if (psiElement instanceof PyNamedParameter) { - PyExpression defaultValue = ((PyNamedParameter)psiElement).getDefaultValue(); - if (defaultValue != null) { - elementsToInvert.add(defaultValue); - } - } } public PsiElement getElementToInvert(PsiElement namedElement, PsiElement element) { @@ -129,6 +122,16 @@ public class PyInvertBooleanDelegate extends InvertBooleanDelegate { return null; } + @Override + public void invertElementInitializer(PsiElement psiElement) { + if (psiElement instanceof PyNamedParameter) { + final PyExpression defaultValue = ((PyNamedParameter)psiElement).getDefaultValue(); + if (defaultValue != null) { + replaceWithNegatedExpression(defaultValue); + } + } + } + @Override public void replaceWithNegatedExpression(PsiElement expression) { if (expression != null && PsiTreeUtil.getParentOfType(expression, PyImportStatementBase.class, false) == null) { diff --git a/python/src/com/jetbrains/python/run/PyTracebackParser.java b/python/src/com/jetbrains/python/run/PyTracebackParser.java index 276de8ed2be3..546daef338dc 100644 --- a/python/src/com/jetbrains/python/run/PyTracebackParser.java +++ b/python/src/com/jetbrains/python/run/PyTracebackParser.java @@ -16,7 +16,7 @@ package com.jetbrains.python.run; import com.jetbrains.python.traceBackParsers.LinkInTrace; -import com.jetbrains.python.traceBackParsers.TraceBackParser; +import com.jetbrains.python.traceBackParsers.TraceBackParserAdapter; import org.jetbrains.annotations.NotNull; import java.util.regex.Matcher; @@ -27,7 +27,7 @@ import java.util.regex.Pattern; * * @author Ilya.Kazakevich */ -public class PyTracebackParser extends TraceBackParser { +public class PyTracebackParser extends TraceBackParserAdapter { public PyTracebackParser() { diff --git a/python/src/com/jetbrains/python/testing/pytest/PyFilesStateMachine.java b/python/src/com/jetbrains/python/testing/pytest/PyFilesStateMachine.java new file mode 100644 index 000000000000..775fed87e4ea --- /dev/null +++ b/python/src/com/jetbrains/python/testing/pytest/PyFilesStateMachine.java @@ -0,0 +1,184 @@ +/* + * 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.jetbrains.python.testing.pytest; + +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Automata searches for file links and numbers. + * Supports 2 modes: "quote-mode" (searches for strings in quotes) and "space mode" -- strings, separated by spaces (or start/end of line) + * Create one providing appropriate flag. + *

    + * Call {@link #addChar(char, int)} or {@link #endLine()} when line ends and do the following: + * if result is true, then machine found something. Use {@link #getFileAndLine()} to get result. + * If false, provide next char + * + * @author Ilya.Kazakevich + */ +final class PyFilesStateMachine { + private final boolean myQuoteMode; // In "quote" mode, spaces accepted + private boolean myLookingForFile = true; // Looking for file if true , for line number if not + private boolean myInProgress; // True if machine in progress, false if waits to start + private boolean myWaitingForCharAfterSemicolon; // Semicolon entered, looking for next char + private int myStart; // start position + private final StringBuilder myFileName = new StringBuilder(); + private final StringBuilder myLineNumber = new StringBuilder(); + + /** + * @param quoteMode start machine in "quote mode" if true + */ + PyFilesStateMachine(final boolean quoteMode) { + myQuoteMode = quoteMode; + + if (!myQuoteMode) { + myInProgress = true; // We can start machine if we do not need to wait for quote + } + } + + /** + * @param charToCheck char to add to machine + * @param charNumber current position of char (to be checked by {@link #getStart()}) + * @return see class doc + */ + boolean addChar(final char charToCheck, final int charNumber) { + if (!myInProgress) { + if ((charToCheck == '"' && myQuoteMode)) { + myInProgress = true; + myStart = charNumber; + } + return false; + } + + if (myWaitingForCharAfterSemicolon) { + // Previous step was ":", what is next? + if (charToCheck == '/' || charToCheck == '\\') { + // Slash? That is part of file + myFileName.append(':'); + myFileName.append(charToCheck); + myWaitingForCharAfterSemicolon = false; + return false; + } + if (Character.isDigit(charToCheck)) { + // Number? Line numbers started, file name found + myLookingForFile = false; + myLineNumber.append(charToCheck); + myWaitingForCharAfterSemicolon = false; + return false; + } + resetState(); + return false; + } + + if (charToCheck == '"') { + // If machine in quote mode, and file and line are already found, machine is finished! + if (myQuoteMode && myFileName.length() > 0 && myLineNumber.length() > 0) { + return true; + } + resetState(); + if (myQuoteMode) { // Relaunch process + myInProgress = true; + myStart = charNumber; + } + return false; + // Can't handle quote in other modes + } + if (charToCheck == ' ') { + if (myQuoteMode && myLookingForFile) { // Spaces in quote mode is legal file names + myFileName.append(' '); + return false; + } + if (!myQuoteMode && myFileName.length() > 0 && myLineNumber.length() > 0) { //In other modes in may indicate end + return true; + } + resetState(); + if (!myQuoteMode) { // Relaunch process if we do not need quote + myInProgress = true; + myStart = charNumber; + } + return false; + } + if (Character.isLetter(charToCheck) || charToCheck == '/' || charToCheck == '\\' || charToCheck == '.' || charToCheck == '_' || + charToCheck == '-') { + // Alpha symbols are or for file name + if (myLookingForFile) { + myFileName.append(charToCheck); + return false; + } + } + if (charToCheck == ':') { + // Semicolon is ok as file name + if (myLookingForFile) { + myWaitingForCharAfterSemicolon = true; + return false; + } + } + if (Character.isDigit(charToCheck)) { + if (!myLookingForFile) { + myLineNumber.append(charToCheck); + return false; + } + if (myFileName.length() == 0) { + // Can't start with digit + resetState(); + return false; + } + myFileName.append(charToCheck); + return false; + } + // Any unknown char is success end if we already have file name and line number + if (myFileName.length() > 0 && myLineNumber.length() > 0) { + return true; + } + resetState(); + return false; + } + + private void resetState() { + myInProgress = false; + myLookingForFile = true; + myLineNumber.setLength(0); + myFileName.setLength(0); + } + + /** + * @return when machine is in {@link PyFilesStateMachineState#FINISHED} use this method to get file and line number + */ + @NotNull + Pair getFileAndLine() { + return Pair.create(myFileName.toString(), myLineNumber.toString()); + } + + /** + * @return position when this machine started + */ + int getStart() { + return myStart; + } + + /** + * To be called when line ends + * + * @return see class doc + */ + boolean endLine() { + if (myQuoteMode) { + return false; + } + return myFileName.length() > 0 && myLineNumber.length() > 0; + } +} diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestTracebackParser.java b/python/src/com/jetbrains/python/testing/pytest/PyTestTracebackParser.java index a4aa1ef566b5..3e69b5e45f33 100644 --- a/python/src/com/jetbrains/python/testing/pytest/PyTestTracebackParser.java +++ b/python/src/com/jetbrains/python/testing/pytest/PyTestTracebackParser.java @@ -15,30 +15,54 @@ */ package com.jetbrains.python.testing.pytest; +import com.intellij.openapi.util.Pair; import com.jetbrains.python.traceBackParsers.LinkInTrace; import com.jetbrains.python.traceBackParsers.TraceBackParser; import org.jetbrains.annotations.NotNull; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import org.jetbrains.annotations.Nullable; /** * User : catherine */ -public final class PyTestTracebackParser extends TraceBackParser { +public final class PyTestTracebackParser implements TraceBackParser { - public PyTestTracebackParser() { - // File name can't start with number, can't be more then 200 chars long (its insane) and line number is also limited to int maxvalue - super(Pattern.compile("([^0-9][^\"]{0,200}?):(\\d{1,8})")); + + @Nullable + @Override + public LinkInTrace findLinkInTrace(@NotNull final String line) { + final PyFilesStateMachine quoteMachine = new PyFilesStateMachine(true); + final PyFilesStateMachine spaceMachine = new PyFilesStateMachine(false); + + + final char[] chars = line.toCharArray(); + for (int i = 0; i < chars.length; i++) { + final char nextChar = chars[i]; + if (quoteMachine.addChar(nextChar, i)) { + return createLinkInTrace(quoteMachine); + } + if (spaceMachine.addChar(nextChar, i)) { + return createLinkInTrace(spaceMachine); + } + } + + // Tell all machines file is ended + if (quoteMachine.endLine()) { + return createLinkInTrace(quoteMachine); + } + if (spaceMachine.endLine()) { + return createLinkInTrace(spaceMachine); + } + + return null; } @NotNull - @Override - protected LinkInTrace findLinkInTrace(@NotNull final String line, @NotNull final Matcher matchedMatcher) { - final String fileName = matchedMatcher.group(1).replace('\\', '/'); - final int lineNumber = Integer.parseInt(matchedMatcher.group(2)); - final int startPos = matchedMatcher.start(1); - final int endPos = matchedMatcher.end(2); - return new LinkInTrace(fileName, lineNumber, startPos, endPos); + private static LinkInTrace createLinkInTrace(@NotNull final PyFilesStateMachine machine) { + final Pair fileAndLine = machine.getFileAndLine(); + final int start = machine.getStart(); + final String lineNumber = fileAndLine.second; + // Cut too long lines that can't be presented as integer anyway + final int number = Integer.parseInt((lineNumber.length() > 5 ? lineNumber.substring(0, 5) : lineNumber)); + return new LinkInTrace(fileAndLine.first, number, start, start + fileAndLine.first.length() + lineNumber.length() + 1); } } diff --git a/python/src/com/jetbrains/python/traceBackParsers/TraceBackParser.java b/python/src/com/jetbrains/python/traceBackParsers/TraceBackParser.java index 276dbd120f18..e187640f6afb 100644 --- a/python/src/com/jetbrains/python/traceBackParsers/TraceBackParser.java +++ b/python/src/com/jetbrains/python/traceBackParsers/TraceBackParser.java @@ -20,36 +20,12 @@ import com.jetbrains.python.testing.pytest.PyTestTracebackParser; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** - * Parent of all traceback filters. Extend it and your impl. to {@link #PARSERS}. - * When implement, provide {@link Pattern pattern}, and your code will be called only if line matches this pattern + * Searches for line in stacktrace * * @author Ilya.Kazakevich */ -public abstract class TraceBackParser { - /** - * We do not search anything in line longer than this, because it makes no sense to search something in so long lines - */ - private static final int MAX_LINE_TO_PARSE = 5000; - @NotNull - private final Pattern myPattern; - - /** - * @param pattern pattern to be used to match line. - */ - protected TraceBackParser(@NotNull final Pattern pattern) { - myPattern = pattern; - } - - - @NotNull // TODO: use EP instead? - @SuppressWarnings("PublicStaticArrayField") // Noone will change it, anyway. - public static final TraceBackParser[] PARSERS = {new PyTestTracebackParser(), new PyTracebackParser()}; - - +public interface TraceBackParser { /** * Searches for link in line * @@ -57,27 +33,9 @@ public abstract class TraceBackParser { * @return line info (if found) */ @Nullable - public final LinkInTrace findLinkInTrace(@NotNull String line) { - if (line.length() > MAX_LINE_TO_PARSE) { - // Cut down line is too long to parse (to prevent freeze) - //noinspection AssignmentToMethodParameter - line = line.substring(0, MAX_LINE_TO_PARSE); - } - final Matcher matcher = myPattern.matcher(line); - if (!matcher.find()) { - return null; - } - return findLinkInTrace(line, matcher); - } + LinkInTrace findLinkInTrace(@NotNull String line); - - /** - * Fetches link from line - * - * @param line line to search link in - * @param matchedMatcher regex matcher that found link - * @return line info - */ - @NotNull - protected abstract LinkInTrace findLinkInTrace(@NotNull String line, @NotNull Matcher matchedMatcher); + @NotNull // TODO: use EP instead? + @SuppressWarnings("PublicStaticArrayField") // Noone will change it, anyway. + TraceBackParser[] PARSERS = {new PyTestTracebackParser(), new PyTracebackParser()}; } diff --git a/python/src/com/jetbrains/python/traceBackParsers/TraceBackParserAdapter.java b/python/src/com/jetbrains/python/traceBackParsers/TraceBackParserAdapter.java new file mode 100644 index 000000000000..db1be45d81b2 --- /dev/null +++ b/python/src/com/jetbrains/python/traceBackParsers/TraceBackParserAdapter.java @@ -0,0 +1,70 @@ +/* + * 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.jetbrains.python.traceBackParsers; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Adapter that uses regexps + * + * @author Ilya.Kazakevich + */ +public abstract class TraceBackParserAdapter implements TraceBackParser { + /** + * We do not search anything in line longer than this, because it makes no sense to search something in so long lines + */ + private static final int MAX_LINE_TO_PARSE = 5000; + @NotNull + private final Pattern myPattern; + + /** + * @param pattern pattern to be used to match line. + */ + protected TraceBackParserAdapter(@NotNull final Pattern pattern) { + myPattern = pattern; + } + + + @Nullable + @Override + public final LinkInTrace findLinkInTrace(@NotNull String line) { + if (line.length() > MAX_LINE_TO_PARSE) { + // Cut down line is too long to parse (to prevent freeze) + //noinspection AssignmentToMethodParameter + line = line.substring(0, MAX_LINE_TO_PARSE); + } + final Matcher matcher = myPattern.matcher(line); + if (!matcher.find()) { + return null; + } + return findLinkInTrace(line, matcher); + } + + + /** + * Fetches link from line + * + * @param line line to search link in + * @param matchedMatcher regex matcher that found link + * @return line info + */ + @NotNull + protected abstract LinkInTrace findLinkInTrace(@NotNull String line, @NotNull Matcher matchedMatcher); +} diff --git a/python/src/com/jetbrains/python/traceBackParsers/package-info.java b/python/src/com/jetbrains/python/traceBackParsers/package-info.java index 5b2f8f35a157..71a5d73f858d 100644 --- a/python/src/com/jetbrains/python/traceBackParsers/package-info.java +++ b/python/src/com/jetbrains/python/traceBackParsers/package-info.java @@ -19,7 +19,7 @@ *

    * Each parser knows how to parse test output to fetch file links from stack traces. * All parsers should extend {@link com.jetbrains.python.traceBackParsers.TraceBackParser} and be installed to - * {@link com.jetbrains.python.traceBackParsers.TraceBackParser#PARSERS} while this functionality has no EP. + * {@link com.jetbrains.python.traceBackParsers.TraceBackParserAdapter#PARSERS} while this functionality has no EP. *

    */ package com.jetbrains.python.traceBackParsers; \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/testing/pytest/PyTestTracebackParserTest.java b/python/testSrc/com/jetbrains/python/testing/pytest/PyTestTracebackParserTest.java index 653ee032c0a3..4863083d1b45 100644 --- a/python/testSrc/com/jetbrains/python/testing/pytest/PyTestTracebackParserTest.java +++ b/python/testSrc/com/jetbrains/python/testing/pytest/PyTestTracebackParserTest.java @@ -15,13 +15,19 @@ */ package com.jetbrains.python.testing.pytest; +import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Base64; import com.jetbrains.python.traceBackParsers.LinkInTrace; +import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import java.nio.charset.Charset; +import java.util.HashSet; +import java.util.Set; + /** * Ensures we can parse pytest traces for links. * This test is JUnit4-based (hence it uses annotations) to support timeout @@ -30,12 +36,14 @@ import org.junit.Test; */ public final class PyTestTracebackParserTest { private String myStringJunk; + private String myStringJunkWithSpaces; + private String myStringJunkWithSpacesAndLine; private String myBase64Junk; @Before public void setUp() throws Exception { // Generate junk to text regexp speed - final int junkSize = 10000; + final int junkSize = 50000; final byte[] junk = new byte[junkSize]; for (int i = 0; i < junkSize; i++) { // We do not care about precision nor security, that junk for tests @@ -45,6 +53,8 @@ public final class PyTestTracebackParserTest { final String longString = StringUtil.repeat("1", junkSize); myStringJunk = String.format("%s:%s", longString, longString); myBase64Junk = Base64.encode(junk); + myStringJunkWithSpaces = StringUtil.repeat("dd ddddddddd", junkSize); + myStringJunkWithSpacesAndLine = myStringJunkWithSpaces + " c:/file:12"; } /** @@ -69,14 +79,48 @@ public final class PyTestTracebackParserTest { Assert.assertNull("No file name provided, but link found", new PyTestTracebackParser().findLinkInTrace(":12 file ")); } - /** - * Ensures - * Regexp worst cases are limited to prevent freezing on very long lines - */ - @Test(timeout = 5000) - public void testExponential() throws Exception { - Assert - .assertNull("No link should be found in numbers list", new PyTestTracebackParser().findLinkInTrace(myStringJunk)); - Assert.assertNull("No link should be found in base64 list", new PyTestTracebackParser().findLinkInTrace(myBase64Junk)); + @Test + public void testLinks() throws java.io.IOException { + final String s = + StreamUtil.readText(PyTestTracebackParserTest.class.getResource("linksDataTest.txt").openStream(), Charset.defaultCharset()); + + final Set requiredStrings = new HashSet(); + requiredStrings.add("file:///c:/windows/system32/file.txt - 42"); + requiredStrings.add("file:///c:/windows/system32/file_spam.txt - 42"); + requiredStrings.add("c:\\documents and settings\\foo.txt - 43"); + requiredStrings.add("file:///c:/windows/system32/file.txt - 42"); + requiredStrings.add("/file.py - 42"); + requiredStrings.add("c:\\folder55\\file.py - 12"); + requiredStrings.add("C:\\temp\\untitled55\\test_sample.py - 5"); + requiredStrings.add("C:\\temp\\untitled55\\test_sample.py - 6"); + requiredStrings.add("C:\\temp\\untitled55\\test_sample.py - 7"); + requiredStrings.add("C:\\temp\\untitled55\\test_sample.py - 89999"); + requiredStrings.add("C:\\temp\\untitled55\\test_sample.py - 99999"); + requiredStrings.add("../../../files/files.py - 100"); + requiredStrings.add("/Users/Mac Hipster/Applications/PyCharm 4.0 .app/helpers/lala.py - 12"); + requiredStrings.add("C:\\Users\\ilya.kazakevich\\virtenvs\\spammy\\lib\\site-packages\\django_cron\\models.py - 4"); + for (final String line : s.split("\n")) { + final LinkInTrace trace = new PyTestTracebackParser().findLinkInTrace(line); + if (trace != null) { + final boolean removeResult = requiredStrings.remove(trace.getFileName() + " - " + trace.getLineNumber()); + Assert.assertTrue(String.format("Unexpected file found %s", trace.getFileName()), + removeResult); + } + } + Assert.assertThat("Some lines were notfound", requiredStrings, Matchers.empty()); + } + + /** + * Ensures + * Regexp worst cases are limited to prevent freezing on very long lines + */ + @Test(timeout = 5000) + public void testLongLines ()throws Exception { + Assert + .assertNull("No link should be found in numbers list", new PyTestTracebackParser().findLinkInTrace(myStringJunk)); + Assert.assertNull("No link should be found in base64 list", new PyTestTracebackParser().findLinkInTrace(myBase64Junk)); + Assert.assertNull("No link should be found in junk", new PyTestTracebackParser().findLinkInTrace(myStringJunkWithSpaces)); + final LinkInTrace trace = new PyTestTracebackParser().findLinkInTrace(myStringJunkWithSpacesAndLine); + Assert.assertNotNull("No link found in long line", trace); + } } -} diff --git a/python/testSrc/com/jetbrains/python/testing/pytest/linksDataTest.txt b/python/testSrc/com/jetbrains/python/testing/pytest/linksDataTest.txt new file mode 100644 index 000000000000..cddc66506b65 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/testing/pytest/linksDataTest.txt @@ -0,0 +1,27 @@ +ddddddddddddddddddddddddddasdasdasdasd +d d d d +ddddddd +/asdasdasd:asdasdasd/asd +dddddddddddddddddd/ddddddddddddd +:asdasdasdasd:asdasd/asaaaaaaaaaaaaaa +file:///c:/windows/system32/file.txt:42 +Here is "c:\documents and settings\foo.txt:43" +And now I will show you the file file:///c:/windows/system32/file_spam.txt:42 +/file.py:42 +../../../files/files.py:100 + +c:\folder55\file.py:12 + +Please check the following file for errors: "/Users/Mac Hipster/Applications/PyCharm 4.0 .app/helpers/lala.py:12" + +"c:\windows\BUHAHA_ERROR:12 +"c:\windows\BUHAHA_ERROR:12 +C:\temp\untitled55\test_sample.py:5: AssertionError +C:\temp\untitled55\test_sample.py:6A AssertionError +C:\temp\untitled55\test_sample.py:7 +C:\temp\untitled55\test_sample.py:8999999999999999 +C:\Users\ilya.kazakevich\virtenvs\spammy\lib\site-packages\django_cron\models.py:4: + +Here is some file C:\temp\untitled55\test_sample.py:999999999999999 +c:/2files.py +a:b:c:d:E:d:s:asd:34:234 \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index be9340fe98ca..faf74426a449 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1575,9 +1575,6 @@ - - diff --git a/xml/impl/src/com/intellij/ide/browsers/actions/BaseOpenInBrowserAction.java b/xml/impl/src/com/intellij/ide/browsers/actions/BaseOpenInBrowserAction.java index 2c50377e2af5..ad74ec9b8b60 100644 --- a/xml/impl/src/com/intellij/ide/browsers/actions/BaseOpenInBrowserAction.java +++ b/xml/impl/src/com/intellij/ide/browsers/actions/BaseOpenInBrowserAction.java @@ -211,7 +211,7 @@ public abstract class BaseOpenInBrowserAction extends DumbAwareAction { public void run() { Url value = (Url)list.getSelectedValue(); if (value == null) { - result.setError(Promise.createError("selected value is null")); + result.setError("selected value is null"); } else { result.setResult(value); diff --git a/xml/impl/src/com/intellij/xml/util/ColorSampleLookupValue.java b/xml/impl/src/com/intellij/xml/util/ColorSampleLookupValue.java index 7b383ddbbedb..ac0a6de6c2b3 100644 --- a/xml/impl/src/com/intellij/xml/util/ColorSampleLookupValue.java +++ b/xml/impl/src/com/intellij/xml/util/ColorSampleLookupValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import com.intellij.codeInsight.lookup.LookupValueWithPriority; import com.intellij.codeInsight.lookup.LookupValueWithUIHint; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Iconable; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.xml.XmlBundle; import org.jetbrains.annotations.NonNls; @@ -141,7 +142,8 @@ public class ColorSampleLookupValue implements LookupValueWithUIHint, DeferredUs @Override @Nullable public String getTypeHint() { - return myValue != null && myValue.charAt(0) == '#' ? myValue : null; + return myName != null && !StringUtil.startsWithChar(myName, '#') + && myValue != null && StringUtil.startsWithChar(myValue, '#') ? myValue : null; } @Override diff --git a/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlElementDescriptorImpl.java b/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlElementDescriptorImpl.java index 25c2566f884b..748aa8ea07ed 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlElementDescriptorImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlElementDescriptorImpl.java @@ -16,10 +16,7 @@ package com.intellij.xml.impl.schema; import com.intellij.codeInsight.daemon.Validator; -import com.intellij.psi.ElementManipulators; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiReference; +import com.intellij.psi.*; import com.intellij.psi.meta.PsiWritableMetaData; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.*; @@ -568,7 +565,14 @@ public class XmlElementDescriptorImpl extends XsdEnumerationDescriptor @Override public String toString() { - return getName() + " (" + getNamespace() + ")"; + String namespace; + try { + namespace = getNamespace(); + } + catch (PsiInvalidElementAccessException e) { + namespace = "!!!Invalid!!!"; + } + return getName() + " (" + namespace + ")"; } @Override diff --git a/xml/xml-psi-impl/src/com/intellij/xml/util/ColorMap.java b/xml/xml-psi-impl/src/com/intellij/xml/util/ColorMap.java index cb352986c931..3a728fbf685b 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/util/ColorMap.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/util/ColorMap.java @@ -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. @@ -208,6 +208,7 @@ public class ColorMap { " \t \tpowderblue \t#b0e0e6 \t176,224,230\n" + " \t \tpurple \t#800080 \t128,0,128\n" + " \t \tred \t#ff0000 \t255,0,0\n" + + " \t \trebeccapurple \t#663399 \t102,51,153\n" + " \t \trosybrown \t#bc8f8f \t188,143,143\n" + " \t \troyalblue \t#4169e1 \t65,105,225\n" + " \t \tsaddlebrown \t#8b4513 \t139,69,19\n" + @@ -244,7 +245,7 @@ public class ColorMap { while (tokenizer.hasMoreTokens()) { String name = tokenizer.nextToken(); - ourSystemColors.add(name.toLowerCase()); + ourSystemColors.add(name.toLowerCase(Locale.US)); tokenizer.nextToken(); } @@ -307,7 +308,7 @@ public class ColorMap { if (StringUtil.isEmptyOrSpaces(text)) { return null; } - String hexValue = text.charAt(0) == '#' ? text : getHexCodeForColorName(text.toLowerCase()); + String hexValue = text.charAt(0) == '#' ? text : getHexCodeForColorName(text.toLowerCase(Locale.US)); if (hexValue != null) { return ColorUtil.fromHex(hexValue, null); } diff --git a/xml/xml-psi-impl/src/com/intellij/xml/util/HtmlUtil.java b/xml/xml-psi-impl/src/com/intellij/xml/util/HtmlUtil.java index 573cc0d6cfcd..e0a866528484 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/util/HtmlUtil.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/util/HtmlUtil.java @@ -323,7 +323,7 @@ public class HtmlUtil { final String tagName = tokenizer.nextToken(); if (tagName.length() == 0) continue; - descriptors[index++] = new XmlElementDescriptorImpl(context instanceof XmlTag ? (XmlTag)context : null) { + descriptors[index++] = new XmlElementDescriptorImpl(null) { @Override public String getName(PsiElement context) { return tagName;