diff --git a/.idea/modules.xml b/.idea/modules.xml index 2c0aba589b39..525c5e308216 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -85,6 +85,7 @@ + @@ -98,8 +99,8 @@ - - + + @@ -157,6 +158,8 @@ + + @@ -189,17 +192,14 @@ + + - - - - - diff --git a/community-resources/src/idea/IdeaApplicationInfo.xml b/community-resources/src/idea/IdeaApplicationInfo.xml index b4e2724e469a..3901a1791f13 100644 --- a/community-resources/src/idea/IdeaApplicationInfo.xml +++ b/community-resources/src/idea/IdeaApplicationInfo.xml @@ -3,8 +3,8 @@ - - + + diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index a89c0760ba4f..015da86e2586 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -910,7 +910,7 @@ public class CompileDriver { if (!outputs.isEmpty()) { final ProgressIndicator indicator = compileContext.getProgressIndicator(); indicator.setText("Synchronizing output directories..."); - lfs.refreshIoFiles(outputs, _status == ExitStatus.CANCELLED, false, null); + CompilerUtil.refreshOutputDirectories(outputs, _status == ExitStatus.CANCELLED); indicator.setText(""); } if (compileContext.isAnnotationProcessorsEnabled() && !myProject.isDisposed()) { diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java index 1417fa69a0e2..9b227da1dbf8 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java @@ -35,7 +35,10 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.RefreshQueue; import com.intellij.pom.java.LanguageLevel; import com.intellij.util.ThrowableRunnable; @@ -128,6 +131,35 @@ public class CompilerUtil { } } + public static void refreshOutputDirectories(Set outputs, boolean async) { + LocalFileSystem fileSystem = LocalFileSystem.getInstance(); + List toRefresh = new ArrayList(); + + int newDirectories = 0; + for (File ioOutput : outputs) { + VirtualFile output = fileSystem.findFileByIoFile(ioOutput); + if (output != null) { + toRefresh.add(output); + } + else if (ioOutput.exists()) { + VirtualFile parent = fileSystem.refreshAndFindFileByIoFile(ioOutput.getParentFile()); + if (parent != null) { + parent.getChildren(); + toRefresh.add(parent); + newDirectories++; + } + } + } + if (newDirectories > 10) { + LOG.info(newDirectories + " new output directories were created, refreshing their parents together to avoid too many rootsChange events"); + RefreshQueue.getInstance().refresh(async, false, null, toRefresh); + } + else { + LOG.debug("Refreshing " + outputs.size() + " outputs"); + fileSystem.refreshIoFiles(outputs, async, false, null); + } + } + public static void refreshIOFile(final File file) { final VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); if (vFile != null) { @@ -234,7 +266,7 @@ public class CompilerUtil { final boolean is3OrNewer = is4OrNewer || isOfVersion(versionString, "1.3"); final boolean is2OrNewer = is3OrNewer || isOfVersion(versionString, "1.2"); final boolean is1OrNewer = is2OrNewer || isOfVersion(versionString, "1.0") || isOfVersion(versionString, "1.1"); - + if (!is1OrNewer) { // unknown jdk version, cannot say anything about the corresponding language level, so leave it unchanged return languageLevel; diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompiler.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompiler.java index b09c8e599626..87136283737b 100644 --- a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompiler.java +++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompiler.java @@ -25,6 +25,10 @@ import org.jetbrains.annotations.NotNull; /** * @author nik + * + * @deprecated this interface is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public abstract class GenericCompiler implements Compiler { protected static final KeyDescriptor STRING_KEY_DESCRIPTOR = new EnumeratorStringDescriptor(); diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassInstrumentingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassInstrumentingCompiler.java index cfcf3373fa3f..4e5143aee495 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassInstrumentingCompiler.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassInstrumentingCompiler.java @@ -21,6 +21,10 @@ package com.intellij.openapi.compiler; * This affects the order of compiler calls: * The sequence in which compilers are called: * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler -> ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator + * + * @deprecated this interface is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public interface ClassInstrumentingCompiler extends FileProcessingCompiler { } diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassPostProcessingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassPostProcessingCompiler.java index 203facd2b02d..c4c3e8b40cf1 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassPostProcessingCompiler.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassPostProcessingCompiler.java @@ -21,6 +21,10 @@ package com.intellij.openapi.compiler; * This affects the order of compiler calls: * The sequence in which compilers are called: * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler -> ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator + * + * @deprecated this interface is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public interface ClassPostProcessingCompiler extends FileProcessingCompiler { } diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java index 4456665ef4bd..03d9be241ffb 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java @@ -63,6 +63,10 @@ public abstract class CompilerManager { * @param compiler compiler implementation * @param inputTypes a set of filetypes that compiler accepts as input * @param outputTypes a set of filetypes that compiler can generate + * + * @deprecated this method is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public abstract void addTranslatingCompiler(@NotNull TranslatingCompiler compiler, Set inputTypes, Set outputTypes); diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CopyingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CopyingCompiler.java index 7d80c21daedf..758524e92426 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/CopyingCompiler.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CopyingCompiler.java @@ -32,6 +32,10 @@ import java.util.List; /** * Compiler which copies the compiled files to a different directory. + * + * @deprecated this interface is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public abstract class CopyingCompiler implements PackagingCompiler{ public abstract VirtualFile[] getFilesToCopy(CompileContext context); diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/GeneratingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/GeneratingCompiler.java index 962bcb218e5e..af1fb1eb8dd7 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/GeneratingCompiler.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/GeneratingCompiler.java @@ -21,6 +21,10 @@ import com.intellij.openapi.vfs.VirtualFile; /** * A base interface for all compilers that generate new files. The generated files may be processed by other compilers. * Actual implementation should implement one of its subinterfaces. Currently only {@link SourceGeneratingCompiler} is available. + * + * @deprecated this interface is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public interface GeneratingCompiler extends Compiler, ValidityStateFactory, IntermediateOutputCompiler { /** diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/JavaSourceTransformingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/JavaSourceTransformingCompiler.java index 27a397569aad..1194efcccba3 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/JavaSourceTransformingCompiler.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/JavaSourceTransformingCompiler.java @@ -19,6 +19,10 @@ import com.intellij.openapi.vfs.VirtualFile; /** * This compiler is called right before the java sources compiler. + * + * @deprecated this interface is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public interface JavaSourceTransformingCompiler extends Compiler { diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/PackagingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/PackagingCompiler.java index e7e1350b41b9..3055e0ff654f 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/PackagingCompiler.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/PackagingCompiler.java @@ -23,6 +23,10 @@ import org.jetbrains.annotations.Nullable; * This affects the order of compiler calls. * The sequence in which compilers are called: * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler -> ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator + * + * @deprecated this interface is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public interface PackagingCompiler extends FileProcessingCompiler{ /** diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceGeneratingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceGeneratingCompiler.java index 4c0b646658f6..2fe36ba202ba 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceGeneratingCompiler.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceGeneratingCompiler.java @@ -23,6 +23,10 @@ import com.intellij.openapi.vfs.VirtualFile; * This affects the order of compiler calls. * The sequence in which compilers are called: * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler -> ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator + * + * @deprecated this interface is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public interface SourceGeneratingCompiler extends GeneratingCompiler { diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/TranslatingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/TranslatingCompiler.java index 3c56f8d9f67d..c5ce012c9ce6 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/TranslatingCompiler.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/TranslatingCompiler.java @@ -26,6 +26,10 @@ import java.util.Collection; * This affects the order of compiler calls. * The sequence in which compilers are called: * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler -> ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator + * + * @deprecated this interface is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public interface TranslatingCompiler extends Compiler { diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstruction.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstruction.java index 148e1524e4a2..eed2e77c8030 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstruction.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstruction.java @@ -15,6 +15,11 @@ */ package com.intellij.openapi.compiler.make; +/** + * @deprecated this class is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. + */ public interface BuildInstruction { String getOutputRelativePath(); diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstructionVisitor.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstructionVisitor.java index 6ae9b33e062b..0bd62be958f7 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstructionVisitor.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstructionVisitor.java @@ -15,7 +15,11 @@ */ package com.intellij.openapi.compiler.make; - +/** + * @deprecated this class is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. + */ public abstract class BuildInstructionVisitor { public boolean visitInstruction(BuildInstruction instruction) throws Exception { return true; diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipant.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipant.java index 48022b6bf272..977772418589 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipant.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipant.java @@ -20,7 +20,9 @@ import com.intellij.packaging.artifacts.Artifact; import org.jetbrains.annotations.Nullable; /** - * @deprecated use interfaces from {@link com.intellij.openapi.compiler.Compiler}'s hierarchy instead + * @deprecated this class is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public abstract class BuildParticipant { public static final BuildParticipant[] EMPTY_ARRAY = new BuildParticipant[0]; diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipantProvider.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipantProvider.java index a0f46da79290..374e161549ad 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipantProvider.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipantProvider.java @@ -24,7 +24,9 @@ import java.util.Collection; /** * @author nik * - * @deprecated use interfaces from {@link com.intellij.openapi.compiler.Compiler}'s hierarchy instead + * @deprecated this class is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. */ public abstract class BuildParticipantProvider { public static final ExtensionPointName EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.compiler.buildParticipantProvider"); diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/FileCopyInstruction.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/FileCopyInstruction.java index 4109410aec00..c5636394cfe0 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/FileCopyInstruction.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/FileCopyInstruction.java @@ -15,10 +15,13 @@ */ package com.intellij.openapi.compiler.make; -import org.jetbrains.annotations.Nullable; - import java.io.File; +/** + * @deprecated this class is part of the obsolete build system which runs as part of the IDE process. Since IDEA 12 plugins need to + * integrate into 'external build system' instead (http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins). + * Since IDEA 13 users cannot switch to the old build system via UI and it will be completely removed in IDEA 14. + */ public interface FileCopyInstruction extends BuildInstruction { File getFile(); diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java index bb74b8eccc45..3a821fb10c0f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java @@ -32,6 +32,7 @@ import com.intellij.openapi.ui.DialogWrapper; import org.jetbrains.annotations.Nullable; public class AdjustArrayRangeAction extends DebuggerAction { + @Override public void actionPerformed(AnActionEvent e) { DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); if(debuggerContext == null) { @@ -73,6 +74,7 @@ public class AdjustArrayRangeAction extends DebuggerAction { if(editor.getExitCode() == DialogWrapper.OK_EXIT_CODE) { debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(debuggerContext.getSuspendContext()) { + @Override public void contextAction() throws Exception { final ValueDescriptorImpl nodeDescriptor = (ValueDescriptorImpl)selectedNode.getDescriptor(); final Renderer lastRenderer = nodeDescriptor.getLastRenderer(); @@ -92,6 +94,7 @@ public class AdjustArrayRangeAction extends DebuggerAction { } } + @Override public void update(AnActionEvent e) { boolean enable = false; DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext()); @@ -139,10 +142,12 @@ public class AdjustArrayRangeAction extends DebuggerAction { myTitle = title; } + @Override public String getDisplayName() { return myTitle; } + @Override public String getHelpTopic() { return null; } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java index b660d5f1ed6e..07bfe319d62f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -72,12 +72,12 @@ import com.intellij.util.EventDispatcher; import com.intellij.util.StringBuilderSpinAllocator; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.HashMap; import com.sun.jdi.*; import com.sun.jdi.connect.*; import com.sun.jdi.request.EventRequest; import com.sun.jdi.request.EventRequestManager; import com.sun.jdi.request.StepRequest; -import gnu.trove.THashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -119,7 +119,10 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb private Map myArguments; private final List myRenderers = new ArrayList(); - private final Map myNodeRenderersMap = new THashMap(); + + // we use null key here + private final Map myNodeRenderersMap = new HashMap(); + private final NodeRendererSettingsListener mySettingsListener = new NodeRendererSettingsListener() { @Override public void renderersChanged() { diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebuggerManagerThreadImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebuggerManagerThreadImpl.java index 4f4eb3f3ffc2..abe71077bc9f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebuggerManagerThreadImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebuggerManagerThreadImpl.java @@ -37,15 +37,16 @@ import org.jetbrains.annotations.TestOnly; * @author lex */ public class DebuggerManagerThreadImpl extends InvokeAndWaitThread implements DebuggerManagerThread, Disposable { - private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebuggerManagerThreadImpl"); + private static final Logger LOG = Logger.getInstance(DebuggerManagerThreadImpl.class); public static final int COMMAND_TIMEOUT = 3000; - private static final int RESTART_TIMEOUT = 500; + private volatile boolean myDisposed; DebuggerManagerThreadImpl(@NotNull Disposable parent) { Disposer.register(parent, this); } + @Override public void dispose() { myDisposed = true; } @@ -63,6 +64,7 @@ public class DebuggerManagerThreadImpl extends InvokeAndWaitThread myResumedThreads; private final EventSet myEventSet; - private volatile boolean myIsResumed; + private volatile boolean myIsResumed; public ConcurrentLinkedQueue myPostponedCommands = new ConcurrentLinkedQueue(); - public volatile boolean myInProgress; - private final HashSet myKeptReferences = new HashSet(); - private EvaluationContextImpl myEvaluationContext = null; + public volatile boolean myInProgress; + private final HashSet myKeptReferences = new HashSet(); + private EvaluationContextImpl myEvaluationContext = null; SuspendContextImpl(@NotNull DebugProcessImpl debugProcess, int suspendPolicy, int eventVotes, EventSet set) { myDebugProcess = debugProcess; @@ -82,7 +82,7 @@ public abstract class SuspendContextImpl implements SuspendContext { try { objectReference.enableCollection(); } - catch (UnsupportedOperationException e) { + catch (UnsupportedOperationException ignored) { // ignore: some J2ME implementations does not provide this operation } } @@ -115,25 +115,30 @@ public abstract class SuspendContextImpl implements SuspendContext { return myEventSet; } + @Override + @NotNull public DebugProcessImpl getDebugProcess() { assertNotResumed(); return myDebugProcess; } + @Override public StackFrameProxyImpl getFrameProxy() { assertNotResumed(); try { return myThread != null && myThread.frameCount() > 0 ? myThread.frame(0) : null; } - catch (EvaluateException e) { + catch (EvaluateException ignored) { return null; } } + @Override public ThreadReferenceProxyImpl getThread() { return myThread; } + @Override public int getSuspendPolicy() { assertNotResumed(); return mySuspendPolicy; @@ -145,7 +150,7 @@ public abstract class SuspendContextImpl implements SuspendContext { } public boolean isExplicitlyResumed(ThreadReferenceProxyImpl thread) { - return myResumedThreads != null ? myResumedThreads.contains(thread) : false; + return myResumedThreads != null && myResumedThreads.contains(thread); } public boolean suspends(ThreadReferenceProxyImpl thread) { @@ -194,7 +199,7 @@ public abstract class SuspendContextImpl implements SuspendContext { try { reference.disableCollection(); } - catch (UnsupportedOperationException e) { + catch (UnsupportedOperationException ignored) { // ignore: some J2ME implementations does not provide this operation } } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java index de161f121d9e..576f781dc368 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java @@ -21,6 +21,7 @@ import com.sun.jdi.InternalException; import com.sun.jdi.ObjectCollectedException; import com.sun.jdi.event.EventSet; import com.sun.jdi.request.EventRequest; +import org.jetbrains.annotations.NotNull; import java.util.*; @@ -33,7 +34,7 @@ public class SuspendManagerImpl implements SuspendManager { private final LinkedList myEventContexts = new LinkedList(); /** * contexts, paused at breakpoint or another debugger event requests. Note that thread, explicitly paused by user is not considered as - * "paused at breakpoint" and JDI prohibits data queries on its stackframes + * "paused at breakpoint" and JDI prohibits data queries on its stack frames */ private final LinkedList myPausedContexts = new LinkedList(); private final Set myFrozenThreads = Collections.synchronizedSet(new HashSet()); @@ -42,9 +43,10 @@ public class SuspendManagerImpl implements SuspendManager { public int suspends = 0; - public SuspendManagerImpl(DebugProcessImpl debugProcess) { + public SuspendManagerImpl(@NotNull DebugProcessImpl debugProcess) { myDebugProcess = debugProcess; myDebugProcess.addDebugProcessListener(new DebugProcessAdapterImpl() { + @Override public void processDetached(DebugProcessImpl process, boolean closedByUser) { myEventContexts.clear(); myPausedContexts.clear(); @@ -53,8 +55,10 @@ public class SuspendManagerImpl implements SuspendManager { }); } + @Override public SuspendContextImpl pushSuspendContext(final int suspendPolicy, int nVotes) { SuspendContextImpl suspendContext = new SuspendContextImpl(myDebugProcess, suspendPolicy, nVotes, null) { + @Override protected void resumeImpl() { if (LOG.isDebugEnabled()) { LOG.debug("Start resuming..."); @@ -71,9 +75,9 @@ public class SuspendManagerImpl implements SuspendManager { catch (InternalException e) { //InternalException 13 means that there are running threads that we are trying to resume //On MacOS it happened that native thread didn't stop while some java thread reached breakpoint + //noinspection StatementWithEmptyBody if (/*Patches.MAC_RESUME_VM_HACK && */e.errorCode() == 13) { //Its funny, but second resume solves the problem - continue; } else { LOG.error(e); @@ -108,8 +112,10 @@ public class SuspendManagerImpl implements SuspendManager { return suspendContext; } + @Override public SuspendContextImpl pushSuspendContext(final EventSet set) { SuspendContextImpl suspendContext = new SuspendContextImpl(myDebugProcess, set.suspendPolicy(), set.size(), set) { + @Override protected void resumeImpl() { if (LOG.isDebugEnabled()) { LOG.debug("Start resuming eventSet " + set.toString() + " suspendPolicy = " + set.suspendPolicy() + ",size = " + set.size()); @@ -138,14 +144,13 @@ public class SuspendManagerImpl implements SuspendManager { catch (ObjectCollectedException e) { // according to error reports set.resume() may throw this if one of the threads has been collected LOG.info(e); - continue; } catch (InternalException e) { //InternalException 13 means that there are running threads that we are trying to resume //On MacOS it happened that native thread didn't stop while some java thread reached breakpoint + //noinspection StatementWithEmptyBody if (/*Patches.MAC_RESUME_VM_HACK && */e.errorCode() == 13 && set.suspendPolicy() == EventRequest.SUSPEND_ALL) { //Its funny, but second resume solves the problem - continue; } else { LOG.error(e); @@ -172,6 +177,7 @@ public class SuspendManagerImpl implements SuspendManager { } } + @Override public void resume(SuspendContextImpl context) { SuspendManagerUtil.prepareForResume(context); @@ -182,6 +188,7 @@ public class SuspendManagerImpl implements SuspendManager { myDebugProcess.clearCashes(suspendPolicy); } + @Override public void popFrame(SuspendContextImpl suspendContext) { popContext(suspendContext); SuspendContextImpl newSuspendContext = pushSuspendContext(suspendContext.getSuspendPolicy(), 0); @@ -189,6 +196,7 @@ public class SuspendManagerImpl implements SuspendManager { notifyPaused(newSuspendContext); } + @Override public SuspendContextImpl getPausedContext() { return !myPausedContexts.isEmpty() ? myPausedContexts.getFirst() : null; } @@ -216,15 +224,18 @@ public class SuspendManagerImpl implements SuspendManager { return myEventContexts.contains(suspendContext); } + @Override public List getEventContexts() { DebuggerManagerThreadImpl.assertIsManagerThread(); return Collections.unmodifiableList(myEventContexts); } + @Override public boolean isFrozen(ThreadReferenceProxyImpl thread) { return myFrozenThreads.contains(thread); } + @Override public boolean isSuspended(ThreadReferenceProxyImpl thread) throws ObjectCollectedException{ DebuggerManagerThreadImpl.assertIsManagerThread(); @@ -249,6 +260,7 @@ public class SuspendManagerImpl implements SuspendManager { return suspended && (thread == null || thread.isSuspended()); } + @Override public void suspendThread(SuspendContextImpl context, ThreadReferenceProxyImpl thread) { LOG.assertTrue(thread != context.getThread(), "Thread is already suspended at the breakpoint"); @@ -258,6 +270,7 @@ public class SuspendManagerImpl implements SuspendManager { } } + @Override public void resumeThread(SuspendContextImpl context, ThreadReferenceProxyImpl thread) { LOG.assertTrue(thread != context.getThread(), "Use resume() instead of resuming breakpoint thread"); LOG.assertTrue(!context.isExplicitlyResumed(thread)); @@ -269,12 +282,14 @@ public class SuspendManagerImpl implements SuspendManager { thread.resume(); } + @Override public void freezeThread(ThreadReferenceProxyImpl thread) { if (myFrozenThreads.add(thread)) { thread.suspend(); } } + @Override public void unfreezeThread(ThreadReferenceProxyImpl thread) { if (myFrozenThreads.remove(thread)) { thread.resume(); @@ -310,6 +325,7 @@ public class SuspendManagerImpl implements SuspendManager { myDebugProcess.myDebugProcessDispatcher.getMulticaster().paused(suspendContext); } + @Override public void voteResume(SuspendContextImpl suspendContext) { if (LOG.isDebugEnabled()) { LOG.debug("Resume voted"); @@ -317,6 +333,7 @@ public class SuspendManagerImpl implements SuspendManager { processVote(suspendContext); } + @Override public void voteSuspend(SuspendContextImpl suspendContext) { suspendContext.myIsVotedForResume = false; processVote(suspendContext); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java index 24babfd066b5..1a6cafc85011 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java @@ -41,33 +41,41 @@ public final class EvaluationContextImpl implements EvaluationContext{ mySuspendContext = suspendContext; } + @Override public Value getThisObject() { return myThisObject; } + @Override public SuspendContextImpl getSuspendContext() { return mySuspendContext; } + @Override public StackFrameProxyImpl getFrameProxy() { return myFrameProxy; } + @NotNull + @Override public DebugProcessImpl getDebugProcess() { return getSuspendContext().getDebugProcess(); } + @Override public Project getProject() { DebugProcessImpl debugProcess = getDebugProcess(); - return debugProcess != null ? debugProcess.getProject() : null; + return debugProcess.getProject(); } + @Override public EvaluationContextImpl createEvaluationContext(Value value) { final EvaluationContextImpl copy = new EvaluationContextImpl(getSuspendContext(), getFrameProxy(), value); copy.setAutoLoadClasses(myAutoLoadClasses); return copy; } + @Override public ClassLoaderReference getClassLoader() throws EvaluateException { DebuggerManagerThreadImpl.assertIsManagerThread(); return myFrameProxy != null ? myFrameProxy.getClassLoader() : null; diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BinaryExpressionEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BinaryExpressionEvaluator.java index db00e47f9295..14d46e7f361b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BinaryExpressionEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BinaryExpressionEvaluator.java @@ -21,6 +21,7 @@ package com.intellij.debugger.engine.evaluation.expression; import com.intellij.debugger.DebuggerBundle; +import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; @@ -45,10 +46,12 @@ class BinaryExpressionEvaluator implements Evaluator { myExpectedType = expectedType; } + @Override public Modifier getModifier() { return null; } + @Override public Object evaluate(EvaluationContextImpl context) throws EvaluateException { Value leftResult = (Value)myLeftOperand.evaluate(context); return evaluateOperation(leftResult, myOpType, myRightOperand, myExpectedType, context); @@ -72,12 +75,12 @@ class BinaryExpressionEvaluator implements Evaluator { } Value rightResult = (Value)rightOperand.evaluate(context); if (opType == JavaTokenType.PLUS) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v1 = ((PrimitiveValue)leftResult).longValue(); final long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 + v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { final double v1 = ((PrimitiveValue)leftResult).doubleValue(); final double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 + v2); @@ -88,19 +91,19 @@ class BinaryExpressionEvaluator implements Evaluator { return DebuggerUtilsEx.createValue(vm, expectedType, v1 + v2); } if (leftResult instanceof StringReference || rightResult instanceof StringReference) { - String v1 = DebuggerUtilsEx.getValueAsString(context, leftResult); - String v2 = DebuggerUtilsEx.getValueAsString(context, rightResult); + String v1 = DebuggerUtils.getValueAsString(context, leftResult); + String v2 = DebuggerUtils.getValueAsString(context, rightResult); return vm.mirrorOf(v1 + v2); } throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "+")); } else if (opType == JavaTokenType.MINUS) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v1 = ((PrimitiveValue)leftResult).longValue(); final long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 - v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { double v1 = ((PrimitiveValue)leftResult).doubleValue(); double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 - v2); @@ -113,12 +116,12 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "-")); } else if (opType == JavaTokenType.ASTERISK) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v1 = ((PrimitiveValue)leftResult).longValue(); final long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 * v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { double v1 = ((PrimitiveValue)leftResult).doubleValue(); double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 * v2); @@ -131,12 +134,12 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "*")); } else if (opType == JavaTokenType.DIV) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { long v1 = ((PrimitiveValue)leftResult).longValue(); long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 / v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { double v1 = ((PrimitiveValue)leftResult).doubleValue(); double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 / v2); @@ -149,12 +152,12 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "/")); } else if (opType == JavaTokenType.PERC) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { long v1 = ((PrimitiveValue)leftResult).longValue(); long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 % v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { double v1 = ((PrimitiveValue)leftResult).doubleValue(); double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 % v2); @@ -167,7 +170,7 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "%")); } else if (opType == JavaTokenType.LTLT) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v2 = ((PrimitiveValue)rightResult).longValue(); if (leftResult instanceof ByteValue) { return DebuggerUtilsEx.createValue(vm, expectedType, ((ByteValue)leftResult).byteValue() << v2); @@ -186,7 +189,7 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "<<")); } else if (opType == JavaTokenType.GTGT) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v2 = ((PrimitiveValue)rightResult).longValue(); if (leftResult instanceof ByteValue) { return DebuggerUtilsEx.createValue(vm, expectedType, ((ByteValue)leftResult).byteValue() >> v2); @@ -205,7 +208,7 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", ">>")); } else if (opType == JavaTokenType.GTGTGT) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v2 = ((PrimitiveValue)rightResult).longValue(); if (leftResult instanceof ByteValue) { return DebuggerUtilsEx.createValue(vm, expectedType, ((ByteValue)leftResult).byteValue() >>> v2); @@ -224,7 +227,7 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", ">>>")); } else if (opType == JavaTokenType.AND) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { long v1 = ((PrimitiveValue)leftResult).longValue(); long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 & v2); @@ -242,7 +245,7 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "&")); } else if (opType == JavaTokenType.OR) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { long v1 = ((PrimitiveValue)leftResult).longValue(); long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 | v2); @@ -260,7 +263,7 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "|")); } else if (opType == JavaTokenType.XOR) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { long v1 = ((PrimitiveValue)leftResult).longValue(); long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 ^ v2); @@ -282,17 +285,17 @@ class BinaryExpressionEvaluator implements Evaluator { return DebuggerUtilsEx.createValue(vm, expectedType, true); } if (leftResult == null) { - return DebuggerUtilsEx.createValue(vm, expectedType, rightResult.equals(leftResult)); + return DebuggerUtilsEx.createValue(vm, expectedType, rightResult.equals(null)); } if (rightResult == null) { - return DebuggerUtilsEx.createValue(vm, expectedType, leftResult.equals(rightResult)); + return DebuggerUtilsEx.createValue(vm, expectedType, leftResult.equals(null)); } - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v1 = ((PrimitiveValue)leftResult).longValue(); final long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 == v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { double v1 = ((PrimitiveValue)leftResult).doubleValue(); double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 == v2); @@ -332,14 +335,14 @@ class BinaryExpressionEvaluator implements Evaluator { } else if (opType == JavaTokenType.NE) { if (leftResult == null && rightResult == null) return DebuggerUtilsEx.createValue(vm, expectedType, false); - if (leftResult == null) return DebuggerUtilsEx.createValue(vm, expectedType, !rightResult.equals(leftResult)); - if (rightResult == null) return DebuggerUtilsEx.createValue(vm, expectedType, !leftResult.equals(rightResult)); - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (leftResult == null) return DebuggerUtilsEx.createValue(vm, expectedType, !rightResult.equals(null)); + if (rightResult == null) return DebuggerUtilsEx.createValue(vm, expectedType, !leftResult.equals(null)); + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v1 = ((PrimitiveValue)leftResult).longValue(); final long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 != v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { double v1 = ((PrimitiveValue)leftResult).doubleValue(); double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 != v2); @@ -362,12 +365,12 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "!=")); } else if (opType == JavaTokenType.LT) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v1 = ((PrimitiveValue)leftResult).longValue(); final long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 < v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { double v1 = ((PrimitiveValue)leftResult).doubleValue(); double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 < v2); @@ -380,12 +383,12 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "<")); } else if (opType == JavaTokenType.GT) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v1 = ((PrimitiveValue)leftResult).longValue(); final long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 > v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { double v1 = ((PrimitiveValue)leftResult).doubleValue(); double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 > v2); @@ -398,12 +401,12 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", ">")); } else if (opType == JavaTokenType.LE) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v1 = ((PrimitiveValue)leftResult).longValue(); final long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 <= v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { double v1 = ((PrimitiveValue)leftResult).doubleValue(); double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 <= v2); @@ -416,12 +419,12 @@ class BinaryExpressionEvaluator implements Evaluator { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", "<=")); } else if (opType == JavaTokenType.GE) { - if (DebuggerUtilsEx.isInteger(leftResult) && DebuggerUtilsEx.isInteger(rightResult)) { + if (DebuggerUtils.isInteger(leftResult) && DebuggerUtils.isInteger(rightResult)) { final long v1 = ((PrimitiveValue)leftResult).longValue(); final long v2 = ((PrimitiveValue)rightResult).longValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 >= v2); } - if (DebuggerUtilsEx.isNumeric(leftResult) && DebuggerUtilsEx.isNumeric(rightResult)) { + if (DebuggerUtils.isNumeric(leftResult) && DebuggerUtils.isNumeric(rightResult)) { double v1 = ((PrimitiveValue)leftResult).doubleValue(); double v2 = ((PrimitiveValue)rightResult).doubleValue(); return DebuggerUtilsEx.createValue(vm, expectedType, v1 >= v2); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java index 038ff6db1b2a..3bfedcecfafe 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java @@ -26,6 +26,7 @@ public abstract class DebuggerCommandImpl extends DebuggerTaskImpl { protected void commandCancelled() { } + @Override public Priority getPriority() { return Priority.LOW; } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerContextCommandImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerContextCommandImpl.java index 076fe6e421ca..e53e92f5d6b8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerContextCommandImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerContextCommandImpl.java @@ -22,14 +22,16 @@ import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.openapi.diagnostic.Logger; import com.sun.jdi.ObjectCollectedException; +import org.jetbrains.annotations.NotNull; public abstract class DebuggerContextCommandImpl extends SuspendContextCommandImpl { private static final Logger LOG = Logger.getInstance(DebuggerContextCommandImpl.class); private final DebuggerContextImpl myDebuggerContext; - protected DebuggerContextCommandImpl(DebuggerContextImpl debuggerContext) { + protected DebuggerContextCommandImpl(@NotNull DebuggerContextImpl debuggerContext) { super(debuggerContext.getSuspendContext()); + myDebuggerContext = debuggerContext; } @@ -40,7 +42,6 @@ public abstract class DebuggerContextCommandImpl extends SuspendContextCommandIm @Override public final void contextAction() throws Exception { final SuspendManager suspendManager = myDebuggerContext.getDebugProcess().getSuspendManager(); - final ThreadReferenceProxyImpl debuggerContextThread = myDebuggerContext.getThreadProxy(); final boolean isSuspendedByContext; try { diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/events/SuspendContextCommandImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/events/SuspendContextCommandImpl.java index 739c96270cca..e3bdc6703859 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/events/SuspendContextCommandImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/events/SuspendContextCommandImpl.java @@ -17,33 +17,32 @@ package com.intellij.debugger.engine.events; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.managerThread.SuspendContextCommand; import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.containers.Stack; +import org.jetbrains.annotations.Nullable; /** - * Created by IntelliJ IDEA. - * User: lex - * Date: Feb 24, 2004 - * Time: 7:01:31 PM * Performs contextAction when evaluation is available in suspend context */ public abstract class SuspendContextCommandImpl extends DebuggerCommandImpl { - private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.SuspendContextCommand"); + private static final Logger LOG = Logger.getInstance(SuspendContextCommand.class); + private final SuspendContextImpl mySuspendContext; - protected SuspendContextCommandImpl(SuspendContextImpl suspendContext) { + protected SuspendContextCommandImpl(@Nullable SuspendContextImpl suspendContext) { mySuspendContext = suspendContext; } public abstract void contextAction() throws Exception; + @Override public final void action() throws Exception { - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("trying " + this); } final SuspendContextImpl suspendContext = getSuspendContext(); - if (suspendContext == null) { if (LOG.isDebugEnabled()) { LOG.debug("skip processing - context is null " + this); @@ -52,12 +51,12 @@ public abstract class SuspendContextCommandImpl extends DebuggerCommandImpl { return; } - if(suspendContext.myInProgress) { + if (suspendContext.myInProgress) { suspendContext.postponeCommand(this); } else { try { - if(!suspendContext.isResumed()) { + if (!suspendContext.isResumed()) { suspendContext.myInProgress = true; contextAction(); } @@ -65,7 +64,7 @@ public abstract class SuspendContextCommandImpl extends DebuggerCommandImpl { notifyCancelled(); } } - finally{ + finally { suspendContext.myInProgress = false; if (suspendContext.isResumed()) { for (SuspendContextCommandImpl postponed = suspendContext.pollPostponedCommand(); postponed != null; postponed = suspendContext.pollPostponedCommand()) { @@ -90,6 +89,7 @@ public abstract class SuspendContextCommandImpl extends DebuggerCommandImpl { } } + @Nullable public SuspendContextImpl getSuspendContext() { return mySuspendContext; } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java index 640f76cad628..6c3568c29347 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java @@ -47,16 +47,23 @@ public final class DebuggerContextImpl implements DebuggerContext { private boolean myInitialized; @Nullable - private final DebuggerSession myDebuggerSession; - private final DebugProcessImpl myDebugProcess; - private final SuspendContextImpl mySuspendContext; + private final DebuggerSession myDebuggerSession; + private final DebugProcessImpl myDebugProcess; + private final SuspendContextImpl mySuspendContext; private final ThreadReferenceProxyImpl myThreadProxy; - private StackFrameProxyImpl myFrameProxy; - private SourcePosition mySourcePosition; - private PsiElement myContextElement; + private StackFrameProxyImpl myFrameProxy; + private SourcePosition mySourcePosition; + private PsiElement myContextElement; - private DebuggerContextImpl(@Nullable DebuggerSession session, DebugProcessImpl debugProcess, SuspendContextImpl context, ThreadReferenceProxyImpl threadProxy, StackFrameProxyImpl frameProxy, SourcePosition position, PsiElement contextElement, boolean initialized) { + private DebuggerContextImpl(@Nullable DebuggerSession session, + @Nullable DebugProcessImpl debugProcess, + @Nullable SuspendContextImpl context, + ThreadReferenceProxyImpl threadProxy, + StackFrameProxyImpl frameProxy, + SourcePosition position, + PsiElement contextElement, + boolean initialized) { LOG.assertTrue(frameProxy == null || threadProxy == null || threadProxy == frameProxy.threadProxy()); LOG.assertTrue(debugProcess != null || frameProxy == null && threadProxy == null); myDebuggerSession = session; @@ -74,6 +81,7 @@ public final class DebuggerContextImpl implements DebuggerContext { return myDebuggerSession; } + @Nullable @Override public DebugProcessImpl getDebugProcess() { return myDebugProcess; @@ -133,7 +141,10 @@ public final class DebuggerContextImpl implements DebuggerContext { return new EvaluationContextImpl(getSuspendContext(), frameProxy, objectReference); } - public static DebuggerContextImpl createDebuggerContext(DebuggerSession session, SuspendContextImpl context, ThreadReferenceProxyImpl threadProxy, StackFrameProxyImpl frameProxy) { + public static DebuggerContextImpl createDebuggerContext(@Nullable DebuggerSession session, + @Nullable SuspendContextImpl context, + ThreadReferenceProxyImpl threadProxy, + StackFrameProxyImpl frameProxy) { LOG.assertTrue(frameProxy == null || threadProxy == null || threadProxy == frameProxy.threadProxy()); LOG.assertTrue(session == null || session.getProcess() != null); return new DebuggerContextImpl(session, session != null ? session.getProcess() : null, context, threadProxy, frameProxy, null, null, context == null); @@ -153,7 +164,7 @@ public final class DebuggerContextImpl implements DebuggerContext { } } - if(myFrameProxy != null) { + if (myFrameProxy != null) { PsiDocumentManager.getInstance(getProject()).commitAndRunReadAction(new Runnable() { @Override public void run() { diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/InvokeThread.java b/java/debugger/impl/src/com/intellij/debugger/impl/InvokeThread.java index 8e782c5ac0a0..8b2d875279da 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/InvokeThread.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/InvokeThread.java @@ -144,10 +144,10 @@ public abstract class InvokeThread { processEvent(myEvents.get()); } - catch (VMDisconnectedException e) { + catch (VMDisconnectedException ignored) { break; } - catch (EventQueueClosedException e) { + catch (EventQueueClosedException ignored) { break; } catch (RuntimeException e) { diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/SimpleStackFrameContext.java b/java/debugger/impl/src/com/intellij/debugger/impl/SimpleStackFrameContext.java index 207c5be2742a..528af2307105 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/SimpleStackFrameContext.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/SimpleStackFrameContext.java @@ -15,9 +15,10 @@ */ package com.intellij.debugger.impl; -import com.intellij.debugger.engine.StackFrameContext; import com.intellij.debugger.engine.DebugProcess; +import com.intellij.debugger.engine.StackFrameContext; import com.intellij.debugger.engine.jdi.StackFrameProxy; +import org.jetbrains.annotations.NotNull; /** * @author Eugene Zhuravlev @@ -32,10 +33,13 @@ public final class SimpleStackFrameContext implements StackFrameContext{ myProcess = process; } + @Override public StackFrameProxy getFrameProxy() { return myProxy; } + @Override + @NotNull public DebugProcess getDebugProcess() { return myProcess; } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArgValueData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArgValueData.java index 3595167f297d..de291e781f5c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArgValueData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArgValueData.java @@ -18,6 +18,7 @@ package com.intellij.debugger.impl.descriptors.data; import com.intellij.debugger.ui.impl.watch.ArgumentValueDescriptorImpl; import com.intellij.openapi.project.Project; import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ArgValueData extends DescriptorData{ @@ -33,7 +34,7 @@ public class ArgValueData extends DescriptorData{ myDisplayName = displayName; } - protected ArgumentValueDescriptorImpl createDescriptorImpl(Project project) { + protected ArgumentValueDescriptorImpl createDescriptorImpl(@NotNull Project project) { return new ArgumentValueDescriptorImpl(project, myIndex, myValue, myDisplayName); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArrayItemData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArrayItemData.java index 7973da61baef..5fc1d293b91a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArrayItemData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ArrayItemData.java @@ -36,7 +36,7 @@ public final class ArrayItemData extends DescriptorData implements DescriptorKey{ private static final Key DESCRIPTOR_DATA = new Key("DESCRIPTOR_DATA"); @@ -25,13 +26,13 @@ public abstract class DescriptorData implements Descr protected DescriptorData() { } - public T createDescriptor(Project project) { + public T createDescriptor(@NotNull Project project) { T descriptor = createDescriptorImpl(project); descriptor.putUserData(DESCRIPTOR_DATA, this); return descriptor; } - protected abstract T createDescriptorImpl(Project project); + protected abstract T createDescriptorImpl(@NotNull Project project); public abstract boolean equals(Object object); diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/FieldData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/FieldData.java index 8392489d8f83..d6b863ca74a0 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/FieldData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/FieldData.java @@ -30,7 +30,7 @@ public final class FieldData extends DescriptorData{ myField = field; } - protected FieldDescriptorImpl createDescriptorImpl(Project project) { + protected FieldDescriptorImpl createDescriptorImpl(@NotNull Project project) { return new FieldDescriptorImpl(project, myObjRef, myField); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/LocalData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/LocalData.java index 3e401a0dc052..576717649d9d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/LocalData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/LocalData.java @@ -19,6 +19,7 @@ import com.intellij.debugger.jdi.LocalVariableProxyImpl; import com.intellij.debugger.ui.impl.watch.LocalVariableDescriptorImpl; import com.intellij.openapi.project.Project; import com.intellij.util.StringBuilderSpinAllocator; +import org.jetbrains.annotations.NotNull; public class LocalData extends DescriptorData{ private final LocalVariableProxyImpl myLocalVariable; @@ -28,7 +29,7 @@ public class LocalData extends DescriptorData{ myLocalVariable = localVariable; } - protected LocalVariableDescriptorImpl createDescriptorImpl(Project project) { + protected LocalVariableDescriptorImpl createDescriptorImpl(@NotNull Project project) { return new LocalVariableDescriptorImpl(project, myLocalVariable); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/MethodReturnValueData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/MethodReturnValueData.java index 1139b2f96d01..f0ffbd751995 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/MethodReturnValueData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/MethodReturnValueData.java @@ -41,7 +41,7 @@ public final class MethodReturnValueData extends DescriptorData{ private final StackFrameProxyImpl myFrame; private final FrameDisplayKey myDisplayKey; private final MethodsTracker myMethodsTracker; - public StackFrameData(StackFrameProxyImpl frame) { + public StackFrameData(@NotNull StackFrameProxyImpl frame) { super(); + myFrame = frame; myDisplayKey = new FrameDisplayKey(NodeManagerImpl.getContextKeyForFrame(frame)); myMethodsTracker = new MethodsTracker(); - } - protected StackFrameDescriptorImpl createDescriptorImpl(Project project) { + @Override + protected StackFrameDescriptorImpl createDescriptorImpl(@NotNull Project project) { return new StackFrameDescriptorImpl(myFrame, myMethodsTracker); } public boolean equals(Object object) { - if(!(object instanceof StackFrameData)) return false; - + if (!(object instanceof StackFrameData)) { + return false; + } return ((StackFrameData)object).myFrame == myFrame; } @@ -49,6 +52,7 @@ public class StackFrameData extends DescriptorData{ return myFrame.hashCode(); } + @Override public DisplayKey getDisplayKey() { return myDisplayKey; } @@ -74,6 +78,5 @@ public class StackFrameData extends DescriptorData{ public int hashCode() { return myContextKey == null? 0 : myContextKey.hashCode(); } - } - + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticData.java index 178954b54a8d..327279e27513 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticData.java @@ -34,7 +34,7 @@ public final class StaticData extends DescriptorData{ return myRefType; } - protected StaticDescriptorImpl createDescriptorImpl(Project project) { + protected StaticDescriptorImpl createDescriptorImpl(@NotNull Project project) { return new StaticDescriptorImpl(myRefType); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticFieldData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticFieldData.java index adfbd3a6314a..156d6647b65f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticFieldData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/StaticFieldData.java @@ -27,7 +27,7 @@ public final class StaticFieldData extends DescriptorData{ myField = field; } - protected FieldDescriptorImpl createDescriptorImpl(Project project) { + protected FieldDescriptorImpl createDescriptorImpl(@NotNull Project project) { return new FieldDescriptorImpl(project, null, myField); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThisData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThisData.java index 9a7afd6ec4b6..43648960b75a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThisData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThisData.java @@ -18,12 +18,13 @@ package com.intellij.debugger.impl.descriptors.data; import com.intellij.debugger.ui.impl.watch.ThisDescriptorImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; +import org.jetbrains.annotations.NotNull; public final class ThisData extends DescriptorData{ private static final Key THIS = new Key("THIS"); - protected ThisDescriptorImpl createDescriptorImpl(Project project) { + protected ThisDescriptorImpl createDescriptorImpl(@NotNull Project project) { return new ThisDescriptorImpl(project); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadData.java index 543ce5327d53..2d1da9aef60c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadData.java @@ -18,6 +18,7 @@ package com.intellij.debugger.impl.descriptors.data; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; public class ThreadData extends DescriptorData { private final ThreadReferenceProxyImpl myThread; @@ -26,7 +27,7 @@ public class ThreadData extends DescriptorData { myThread = thread; } - protected ThreadDescriptorImpl createDescriptorImpl(Project project) { + protected ThreadDescriptorImpl createDescriptorImpl(@NotNull Project project) { return new ThreadDescriptorImpl(myThread); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadGroupData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadGroupData.java index fef933316a8e..fd2a62f2982d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadGroupData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThreadGroupData.java @@ -18,6 +18,7 @@ package com.intellij.debugger.impl.descriptors.data; import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl; import com.intellij.debugger.ui.impl.watch.ThreadGroupDescriptorImpl; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; public class ThreadGroupData extends DescriptorData{ private final ThreadGroupReferenceProxyImpl myThreadGroup; @@ -27,7 +28,7 @@ public class ThreadGroupData extends DescriptorData{ myThreadGroup = threadGroup; } - protected ThreadGroupDescriptorImpl createDescriptorImpl(Project project) { + protected ThreadGroupDescriptorImpl createDescriptorImpl(@NotNull Project project) { return new ThreadGroupDescriptorImpl(myThreadGroup); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThrownExceptionValueData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThrownExceptionValueData.java index 5c8610255f22..6ca40003b85a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThrownExceptionValueData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThrownExceptionValueData.java @@ -28,7 +28,7 @@ public final class ThrownExceptionValueData extends DescriptorData{ private final ValueDescriptorImpl myParentDescriptor; @@ -35,7 +36,7 @@ public class UserExpressionData extends DescriptorData myText = text; } - protected UserExpressionDescriptorImpl createDescriptorImpl(Project project) { + protected UserExpressionDescriptorImpl createDescriptorImpl(@NotNull Project project) { return new UserExpressionDescriptorImpl(project, myParentDescriptor, myTypeName, myName, myText); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/WatchItemData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/WatchItemData.java index 4b44a0d060db..902973048f30 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/WatchItemData.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/WatchItemData.java @@ -19,6 +19,7 @@ import com.intellij.debugger.engine.evaluation.TextWithImports; import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor; import com.intellij.openapi.project.Project; import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -34,7 +35,7 @@ public final class WatchItemData extends DescriptorData{ myValue = value; } - protected WatchItemDescriptor createDescriptorImpl(final Project project) { + protected WatchItemDescriptor createDescriptorImpl(@NotNull final Project project) { return new WatchItemDescriptor(project, myText, myValue); } diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariableProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariableProxyImpl.java index 0ccda00e2a4b..cca6b8029865 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariableProxyImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariableProxyImpl.java @@ -40,6 +40,7 @@ public class LocalVariableProxyImpl extends JdiProxy implements LocalVariablePro myVariable = variable; } + @Override protected void clearCaches() { myVariable = null; myVariableType = null; @@ -47,10 +48,9 @@ public class LocalVariableProxyImpl extends JdiProxy implements LocalVariablePro public LocalVariable getVariable() throws EvaluateException { checkValid(); - if(myVariable == null) { + if (myVariable == null) { myVariable = myFrame.visibleVariableByNameInt(myVariableName); - - if(myVariable == null) { + if (myVariable == null) { //myFrame is not this variable's frame throw EvaluateExceptionUtil.createEvaluateException(new IncompatibleThreadStateException()); } @@ -75,7 +75,7 @@ public class LocalVariableProxyImpl extends JdiProxy implements LocalVariablePro } public boolean equals(Object o) { - if(o instanceof LocalVariableProxyImpl) { + if (o instanceof LocalVariableProxyImpl) { LocalVariableProxyImpl proxy = (LocalVariableProxyImpl)o; return Comparing.equal(proxy.myFrame, myFrame) && myVariableName.equals(proxy.myVariableName); } diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/ObjectReferenceProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/ObjectReferenceProxyImpl.java index 4e395995fe5d..373404b9a0a7 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/ObjectReferenceProxyImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/ObjectReferenceProxyImpl.java @@ -87,7 +87,7 @@ public class ObjectReferenceProxyImpl extends JdiProxy { try { myIsCollected = Boolean.valueOf(VirtualMachineProxyImpl.isCollected(myObjectReference)); } - catch (VMDisconnectedException e) { + catch (VMDisconnectedException ignored) { myIsCollected = Boolean.TRUE; } } @@ -128,7 +128,7 @@ public class ObjectReferenceProxyImpl extends JdiProxy { if(this == o) return true; ObjectReference ref = myObjectReference; - return ref != null && ref.equals(((ObjectReferenceProxyImpl)o).myObjectReference); + return ref.equals(((ObjectReferenceProxyImpl)o).myObjectReference); } @@ -139,9 +139,10 @@ public class ObjectReferenceProxyImpl extends JdiProxy { /** * The advice to the proxy to clear cached data. */ + @Override protected void clearCaches() { if (Boolean.FALSE.equals(myIsCollected)) { - // clearing cache makes sence only if the object has not been collected yet + // clearing cache makes sense only if the object has not been collected yet myIsCollected = null; } } diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java index 5fed47929af3..a7fbffa5906e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java @@ -107,7 +107,7 @@ public class StackFrameProxyImpl extends JdiProxy implements StackFrameProxy { checkValid(); - if(myStackFrame == null) { + if (myStackFrame == null) { try { final ThreadReference threadRef = myThreadProxy.getThreadReference(); myStackFrame = threadRef.frame(getFrameIndex()); diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java index 594e7a2ddd60..74b17865270d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java @@ -35,14 +35,15 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp // cached data private String myName; private int myFrameCount = -1; - // stackframes, 0 - bottom + // stack frames, 0 - bottom private final List myFramesFromBottom = new ArrayList(); //cache build on the base of myFramesFromBottom 0 - top, initially nothing is cached private List myFrames = null; private ThreadGroupReferenceProxyImpl myThreadGroupProxy; - public static Comparator ourComparator = new Comparator() { + public static final Comparator ourComparator = new Comparator() { + @Override public int compare(ThreadReferenceProxyImpl th1, ThreadReferenceProxyImpl th2) { return th1.name().compareToIgnoreCase(th2.name()); } @@ -52,11 +53,13 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp super(virtualMachineProxy, threadReference); } + @Override public ThreadReference getThreadReference() { DebuggerManagerThreadImpl.assertIsManagerThread(); return (ThreadReference)getObjectReference(); } + @Override public VirtualMachineProxyImpl getVirtualMachine() { DebuggerManagerThreadImpl.assertIsManagerThread(); return (VirtualMachineProxyImpl) myTimer; @@ -68,10 +71,10 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp try { myName = getThreadReference().name(); } - catch (ObjectCollectedException e) { + catch (ObjectCollectedException ignored) { myName = ""; } - catch (IllegalThreadStateException e) { + catch (IllegalThreadStateException ignored) { myName = "zombie"; } } @@ -84,7 +87,7 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp try { return getThreadReference().suspendCount(); } - catch (ObjectCollectedException e) { + catch (ObjectCollectedException ignored) { return 0; } } @@ -106,7 +109,7 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp try { threadRefString = getThreadReference().toString() ; } - catch (ObjectCollectedException e) { + catch (ObjectCollectedException ignored) { threadRefString = "[thread collected]"; } return "ThreadReferenceProxyImpl: " + threadRefString + " " + super.toString(); @@ -127,6 +130,7 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp } } + @Override protected void clearCaches() { DebuggerManagerThreadImpl.assertIsManagerThread(); myName = null; @@ -139,7 +143,7 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp try { return getThreadReference().status(); } - catch (ObjectCollectedException e) { + catch (ObjectCollectedException ignored) { return ThreadReference.THREAD_STATUS_ZOMBIE; } } @@ -152,7 +156,7 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp try { threadGroupRef = getThreadReference().threadGroup(); } - catch (ObjectCollectedException e) { + catch (ObjectCollectedException ignored) { threadGroupRef = null; } myThreadGroupProxy = getVirtualMachineProxy().getThreadGroupReferenceProxy(threadGroupRef); @@ -160,6 +164,7 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp return myThreadGroupProxy; } + @Override public int frameCount() throws EvaluateException { DebuggerManagerThreadImpl.assertIsManagerThread(); checkValid(); @@ -168,7 +173,7 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp try { myFrameCount = threadReference.frameCount(); } - catch(ObjectCollectedException e) { + catch(ObjectCollectedException ignored) { myFrameCount = 0; } catch (IncompatibleThreadStateException e) { @@ -176,7 +181,7 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp try { isSuspended = threadReference.isSuspended(); } - catch (Throwable th) { + catch (Throwable ignored) { // unable to determine whether the thread is actually suspended, so propagating original exception throw EvaluateExceptionUtil.createEvaluateException(e); } @@ -207,12 +212,11 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp myFrames = new ArrayList(frameCount()); for (ListIterator iterator = myFramesFromBottom.listIterator(frameCount()); iterator.hasPrevious();) { - StackFrameProxyImpl stackFrameProxy = iterator.previous(); - myFrames.add(stackFrameProxy); + myFrames.add(iterator.previous()); } } } - catch (ObjectCollectedException e) { + catch (ObjectCollectedException ignored) { return Collections.emptyList(); } return myFrames; @@ -234,13 +238,13 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp int index = myFramesFromBottom.size() + 1; for (ListIterator iterator = frames.listIterator(count - myFramesFromBottom.size()); iterator.hasPrevious();) { - StackFrame stackFrame = iterator.previous(); - myFramesFromBottom.add(new StackFrameProxyImpl(this, stackFrame, index)); + myFramesFromBottom.add(new StackFrameProxyImpl(this, iterator.previous(), index)); index++; } } } + @Override public StackFrameProxyImpl frame(int i) throws EvaluateException { DebuggerManagerThreadImpl.assertIsManagerThread(); final ThreadReference threadReference = getThreadReference(); @@ -255,10 +259,10 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp } return myFramesFromBottom.get(frameCount - i - 1); } - catch (ObjectCollectedException e) { + catch (ObjectCollectedException ignored) { return null; } - catch (IllegalThreadStateException e) { + catch (IllegalThreadStateException ignored) { return null; } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/FramesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/FramesPanel.java index 6ed904d74bb9..52bcf8acce32 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/FramesPanel.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/FramesPanel.java @@ -59,6 +59,7 @@ import com.intellij.ui.components.panels.Wrapper; import com.intellij.util.Alarm; import com.sun.jdi.ObjectCollectedException; import com.sun.jdi.VMDisconnectedException; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.border.EmptyBorder; @@ -102,6 +103,7 @@ public class FramesPanel extends UpdatableDebuggerView { myFramesList.addListSelectionListener(myFramesListener); myFramesList.addMouseListener(new MouseAdapter() { + @Override public void mousePressed(final MouseEvent e) { int index = myFramesList.locationToIndex(e.getPoint()); if (index >= 0 && myFramesList.isSelectedIndex(index)) { @@ -140,6 +142,7 @@ public class FramesPanel extends UpdatableDebuggerView { return toolbar; } + @Override public DebuggerStateManager getContextManager() { return myStateManager; } @@ -151,6 +154,7 @@ public class FramesPanel extends UpdatableDebuggerView { myIsEnabled = enabled; } + @Override public void valueChanged(ListSelectionEvent e) { if (!myIsEnabled || e.getValueIsAdjusting()) { return; @@ -169,6 +173,7 @@ public class FramesPanel extends UpdatableDebuggerView { private void registerThreadsPopupMenu(final JList framesList) { final PopupHandler popupHandler = new PopupHandler() { + @Override public void invokePopup(Component comp, int x, int y) { DefaultActionGroup group = (DefaultActionGroup)ActionManager.getInstance().getAction(DebuggerActions.THREADS_PANEL_POPUP); ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(DebuggerActions.THREADS_PANEL_POPUP, group); @@ -177,6 +182,7 @@ public class FramesPanel extends UpdatableDebuggerView { }; framesList.addMouseListener(popupHandler); registerDisposable(new Disposable() { + @Override public void dispose() { myThreadsCombo.removeItemListener(myThreadsListener); framesList.removeMouseListener(popupHandler); @@ -191,6 +197,7 @@ public class FramesPanel extends UpdatableDebuggerView { myIsEnabled = enabled; } + @Override public void itemStateChanged(ItemEvent e) { if (!myIsEnabled) return; if (e.getStateChange() == ItemEvent.SELECTED) { @@ -202,6 +209,7 @@ public class FramesPanel extends UpdatableDebuggerView { private final AtomicBoolean myPerformFullRebuild = new AtomicBoolean(false); + @Override protected void rebuild(int event) { myRebuildAlarm.cancelAllRequests(); final boolean isRefresh = event == DebuggerSession.EVENT_REFRESH || @@ -211,12 +219,12 @@ public class FramesPanel extends UpdatableDebuggerView { myPerformFullRebuild.set(true); } myRebuildAlarm.addRequest(new Runnable() { + @Override public void run() { try { doRebuild(!myPerformFullRebuild.getAndSet(false)); } - catch (VMDisconnectedException e) { - // ignored + catch (VMDisconnectedException ignored) { } } }, 100, ModalityState.NON_MODAL); @@ -298,6 +306,7 @@ public class FramesPanel extends UpdatableDebuggerView { return descriptors; } + @Override public void threadAction() { if (myRefreshOnly && myThreadDescriptorsToUpdate.length != myThreadsCombo.getItemCount()) { // there is no sense in refreshing combobox if thread list has changed since creation of this command @@ -329,6 +338,7 @@ public class FramesPanel extends UpdatableDebuggerView { descriptor.updateRepresentation(evaluationContext, DescriptorLabelListener.DUMMY_LISTENER); } DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() { + @Override public void run() { try { myThreadsListener.setEnabled(false); @@ -346,6 +356,7 @@ public class FramesPanel extends UpdatableDebuggerView { } } + @Override protected void commandCancelled() { if (!DebuggerManagerThreadImpl.isManagerThread()) { return; @@ -372,6 +383,7 @@ public class FramesPanel extends UpdatableDebuggerView { private void refillThreadsCombo(final ThreadReferenceProxyImpl threadToSelect) { final List threadItems = createThreadDescriptorsList(); DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() { + @Override public void run() { try { myThreadsListener.setEnabled(false); @@ -400,9 +412,11 @@ public class FramesPanel extends UpdatableDebuggerView { myDebuggerContext = debuggerContext; } + @Override public void contextAction() throws Exception { updateFrameList(myDebuggerContext.getThreadProxy()); DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() { + @Override public void run() { try { myFramesListener.setEnabled(false); @@ -425,7 +439,7 @@ public class FramesPanel extends UpdatableDebuggerView { return; } } - catch (ObjectCollectedException e) { + catch (ObjectCollectedException ignored) { return; } @@ -462,11 +476,13 @@ public class FramesPanel extends UpdatableDebuggerView { myDebuggerContext = debuggerContext; } + @Override public void contextAction() throws Exception { final ThreadReferenceProxyImpl thread = myDebuggerContext.getThreadProxy(); try { if(!getSuspendContext().getDebugProcess().getSuspendManager().isSuspended(thread)) { DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() { + @Override public void run() { try { myFramesListener.setEnabled(false); @@ -491,7 +507,7 @@ public class FramesPanel extends UpdatableDebuggerView { return; } } - catch (ObjectCollectedException e) { + catch (ObjectCollectedException ignored) { return; } @@ -499,7 +515,7 @@ public class FramesPanel extends UpdatableDebuggerView { try { frames = thread.frames(); } - catch (EvaluateException e) { + catch (EvaluateException ignored) { frames = Collections.emptyList(); } @@ -601,7 +617,7 @@ public class FramesPanel extends UpdatableDebuggerView { private final long myTimestamp; private final IndexCounter myCounter; - public AppendFrameCommand(SuspendContextImpl suspendContext, StackFrameProxyImpl frame, EvaluationContextImpl evaluationContext, + public AppendFrameCommand(@NotNull SuspendContextImpl suspendContext, @NotNull StackFrameProxyImpl frame, EvaluationContextImpl evaluationContext, MethodsTracker tracker, int indexToInsert, final boolean isContextFrame, final long timestamp, IndexCounter counter) { super(suspendContext); myFrame = frame; @@ -613,12 +629,14 @@ public class FramesPanel extends UpdatableDebuggerView { myCounter = counter; } + @Override public void contextAction() throws Exception { final StackFrameDescriptorImpl descriptor = new StackFrameDescriptorImpl(myFrame, myTracker); descriptor.setContext(myEvaluationContext); descriptor.updateRepresentation(myEvaluationContext, DescriptorLabelListener.DUMMY_LISTENER); final Project project = getProject(); DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() { + @Override public void run() { try { myFramesListener.setEnabled(false); @@ -650,6 +668,7 @@ public class FramesPanel extends UpdatableDebuggerView { } } + @Override public void requestFocus() { myFramesList.requestFocus(); } @@ -672,6 +691,7 @@ public class FramesPanel extends UpdatableDebuggerView { myShouldShow = DebuggerSettings.getInstance().SHOW_LIBRARY_STACKFRAMES; } + @Override public void update(final AnActionEvent e) { super.update(e); final Presentation presentation = e.getPresentation(); @@ -679,10 +699,12 @@ public class FramesPanel extends UpdatableDebuggerView { presentation.setText(shouldShow ? ourTextWhenShowIsOn : ourTextWhenShowIsOff); } + @Override public boolean isSelected(AnActionEvent e) { return !myShouldShow; } + @Override public void setSelected(AnActionEvent e, boolean enabled) { myShouldShow = !enabled; DebuggerSettings.getInstance().SHOW_LIBRARY_STACKFRAMES = myShouldShow; diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java b/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java index 46f1515f1ea5..3b300fca2257 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java @@ -31,6 +31,7 @@ import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.xdebugger.AbstractDebuggerSession; +import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; import com.intellij.xdebugger.impl.DebuggerSupport; import com.intellij.xdebugger.impl.actions.DebuggerActionHandler; @@ -41,6 +42,7 @@ import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointItem; import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointPanelProvider; import com.intellij.xdebugger.impl.evaluate.quick.common.QuickEvaluateHandler; import com.intellij.xdebugger.impl.settings.DebuggerSettingsPanelProvider; +import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -194,7 +196,7 @@ public class JavaDebuggerSupport extends DebuggerSupport { @NotNull @Override public EditBreakpointActionHandler getEditBreakpointAction() { - return DISABLED_EDIT; + return X_EDIT; } @Override @@ -374,10 +376,10 @@ public class JavaDebuggerSupport extends DebuggerSupport { } }; - private static final EditBreakpointActionHandler DISABLED_EDIT = new EditBreakpointActionHandler() { + private static final EditBreakpointActionHandler X_EDIT = new EditBreakpointActionHandler() { @Override protected void doShowPopup(Project project, JComponent component, Point whereToShow, Object breakpoint) { - + DebuggerUIUtil.showXBreakpointEditorBalloon(project, whereToShow, component, false, (XBreakpoint)breakpoint); } @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java index 76c55c7b4ae9..5591c9938e14 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java @@ -56,6 +56,7 @@ import com.intellij.xdebugger.impl.DebuggerSupport; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.breakpoints.XBreakpointManagerImpl; import com.intellij.xdebugger.impl.breakpoints.XDependentBreakpointManager; +import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl; import com.sun.jdi.InternalException; import com.sun.jdi.ThreadReference; import com.sun.jdi.request.*; @@ -173,13 +174,16 @@ public class BreakpointManager { DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() { @Override public void run() { - final RangeHighlighter highlighter = ((BreakpointWithHighlighter)breakpoint).getHighlighter(); - if (highlighter != null) { - final GutterIconRenderer renderer = highlighter.getGutterIconRenderer(); - if (renderer != null) { - DebuggerSupport.getDebuggerSupport(JavaDebuggerSupport.class).getEditBreakpointAction().editBreakpoint( - myProject, editor, breakpoint, renderer - ); + XBreakpoint xBreakpoint = breakpoint.myXBreakpoint; + if (xBreakpoint instanceof XLineBreakpointImpl) { + RangeHighlighter highlighter = ((XLineBreakpointImpl)xBreakpoint).getHighlighter(); + if (highlighter != null) { + GutterIconRenderer renderer = highlighter.getGutterIconRenderer(); + if (renderer != null) { + DebuggerSupport.getDebuggerSupport(JavaDebuggerSupport.class).getEditBreakpointAction().editBreakpoint( + myProject, editor, breakpoint.myXBreakpoint, renderer + ); + } } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameVariablesTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameVariablesTree.java index 8722246939bb..5694eb1cba67 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameVariablesTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameVariablesTree.java @@ -65,7 +65,7 @@ import javax.swing.tree.TreePath; import java.util.*; public class FrameVariablesTree extends DebuggerTree { - private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.FrameDebuggerTree"); + private static final Logger LOG = Logger.getInstance(FrameVariablesTree.class); private boolean myAnyNewLocals; private boolean myAutoWatchMode = false; @@ -122,7 +122,7 @@ public class FrameVariablesTree extends DebuggerTree { @Override - protected DebuggerCommandImpl getBuildNodeCommand(final DebuggerTreeNodeImpl node) { + protected DebuggerCommandImpl getBuildNodeCommand(@NotNull DebuggerTreeNodeImpl node) { if (node.getDescriptor() instanceof StackFrameDescriptorImpl) { return new BuildFrameTreeVariablesCommand(node); } @@ -192,9 +192,6 @@ public class FrameVariablesTree extends DebuggerTree { catch (EvaluateException e) { if (e.getCause() instanceof AbsentInformationException) { final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy(); - if (frame == null) { - throw e; - } final Collection argValues = frame.getArgumentValues(); int index = 0; @@ -281,9 +278,6 @@ public class FrameVariablesTree extends DebuggerTree { private static Map getVisibleVariables(final StackFrameDescriptorImpl stackDescriptor) throws EvaluateException { final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy(); - if (frame == null) { - return Collections.emptyMap(); - } final Map vars = new HashMap(); for (LocalVariableProxyImpl localVariableProxy : frame.visibleVariables()) { vars.put(localVariableProxy.name(), localVariableProxy); @@ -442,7 +436,6 @@ public class FrameVariablesTree extends DebuggerTree { try { StackFrameProxyImpl frame = debuggerContext.getFrameProxy(); - if (frame != null) { NodeManagerImpl nodeManager = getNodeFactory(); rootNode = nodeManager.createNode(nodeManager.getStackFrameDescriptor(null, frame), debuggerContext.createEvaluationContext()); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java index 71e5ff878d70..ca015a9929b2 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java @@ -26,6 +26,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; +import com.intellij.ui.AppUIUtil; import com.intellij.ui.ScrollPaneFactory; import com.intellij.util.SystemProperties; import com.intellij.xdebugger.XSourcePosition; @@ -60,6 +61,7 @@ public class VariablesPanel extends DebuggerTreePanel implements DataProvider { myCards.add(frameTree, TREE); myXTree = new MyXVariablesView(project); + registerDisposable(myXTree); myCards.add(myXTree.getTree(), X_TREE); add(ScrollPaneFactory.createScrollPane(myCards), BorderLayout.CENTER); @@ -101,15 +103,20 @@ public class VariablesPanel extends DebuggerTreePanel implements DataProvider { return (FrameVariablesTree)getTree(); } - public void stackChanged(@Nullable XStackFrame xStackFrame) { - myXTree.stackChanged(xStackFrame); - ((CardLayout)(myCards.getLayout())).show(myCards, xStackFrame == null ? TREE : X_TREE); + public void stackChanged(@Nullable final XStackFrame xStackFrame) { + AppUIUtil.invokeOnEdt(new Runnable() { + @Override + public void run() { + myXTree.stackChanged(xStackFrame); + ((CardLayout)(myCards.getLayout())).show(myCards, xStackFrame == null ? TREE : X_TREE); + } + }); } private static final class MyXVariablesView extends XVariablesViewBase { private XStackFrame myCurrentXStackFrame; - public MyXVariablesView(Project project) { + public MyXVariablesView(@NotNull Project project) { super(project, new XDebuggerEditorsProvider() { @NotNull @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java index 1b1033a4b263..e5d5b79ebd29 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java @@ -68,7 +68,7 @@ import java.util.*; import java.util.List; public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvider { - private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.watch.DebuggerTree"); + private static final Logger LOG = Logger.getInstance(DebuggerTree.class); protected static final Key VISIBLE_RECT = Key.create("VISIBLE_RECT"); public static final DataKey DATA_KEY = DataKey.create("DebuggerTree"); @@ -444,9 +444,6 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi try { final StackFrameDescriptorImpl stackDescriptor = (StackFrameDescriptorImpl)getNode().getDescriptor(); final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy(); - if (frame == null) { - return; - } final DebuggerContextImpl debuggerContext = getDebuggerContext(); final EvaluationContextImpl evaluationContext = debuggerContext.createEvaluationContext(); @@ -538,12 +535,10 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi protected void buildVariables(final StackFrameDescriptorImpl stackDescriptor, final EvaluationContextImpl evaluationContext) throws EvaluateException { final StackFrameProxyImpl frame = stackDescriptor.getFrameProxy(); - if (frame != null) { - for (final LocalVariableProxyImpl local : frame.visibleVariables()) { - final LocalVariableDescriptorImpl localVariableDescriptor = myNodeManager.getLocalVariableDescriptor(stackDescriptor, local); - final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(localVariableDescriptor, evaluationContext); - myChildren.add(variableNode); - } + for (final LocalVariableProxyImpl local : frame.visibleVariables()) { + final LocalVariableDescriptorImpl localVariableDescriptor = myNodeManager.getLocalVariableDescriptor(stackDescriptor, local); + final DebuggerTreeNodeImpl variableNode = myNodeManager.createNode(localVariableDescriptor, evaluationContext); + myChildren.add(variableNode); } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java index 96f0dc6ff61f..f93ab91056b8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java @@ -44,7 +44,6 @@ public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements private boolean myIsPrimitive; private boolean myIsNewLocal = true; - private boolean myIsVisible = true; public LocalVariableDescriptorImpl(Project project, @NotNull LocalVariableProxyImpl local) { @@ -54,6 +53,7 @@ public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements myLocalVariable = local; } + @Override public LocalVariableProxyImpl getLocalVariable() { return myLocalVariable; } @@ -83,13 +83,15 @@ public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements return myIsNewLocal; } + @Override public boolean isPrimitive() { return myIsPrimitive; } + @Override public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException { - myIsVisible = myFrameProxy.isLocalVariableVisible(getLocalVariable()); - if (myIsVisible) { + boolean isVisible = myFrameProxy.isLocalVariableVisible(getLocalVariable()); + if (isVisible) { final String typeName = getLocalVariable().typeName(); myTypeName = typeName; myIsPrimitive = DebuggerUtils.isPrimitiveType(typeName); @@ -103,6 +105,7 @@ public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements myIsNewLocal = aNew; } + @Override public void displayAs(NodeDescriptor descriptor) { super.displayAs(descriptor); if(descriptor instanceof LocalVariableDescriptorImpl) { @@ -110,10 +113,12 @@ public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements } } + @Override public String getName() { return myLocalVariable.name(); } + @Override public String calcValueName() { final ClassRenderer classRenderer = NodeRendererSettings.getInstance().getClassRenderer(); StringBuilder buf = StringBuilderSpinAllocator.alloc(); @@ -130,6 +135,7 @@ public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements } } + @Override public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException { PsiElementFactory elementFactory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory(); try { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java index a98afc7c9eb8..54e4826f8d1d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java @@ -32,6 +32,7 @@ import com.intellij.debugger.ui.tree.UserExpressionDescriptor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.sun.jdi.*; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -97,6 +98,7 @@ public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory { final MarkedDescriptorTree displayDescriptorTree = new MarkedDescriptorTree(); tree.dfst(new DescriptorTree.DFSTWalker() { + @Override public void visit(NodeDescriptor parent, NodeDescriptor child) { final DescriptorData descriptorData = DescriptorData.getDescriptorData(child); descriptorTree.addChild(parent, child, descriptorData); @@ -130,10 +132,12 @@ public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory { return descriptorTree; } + @Override public ArrayElementDescriptorImpl getArrayItemDescriptor(NodeDescriptor parent, ArrayReference array, int index) { return getDescriptor(parent, new ArrayItemData(array, index)); } + @Override public FieldDescriptorImpl getFieldDescriptor(NodeDescriptor parent, ObjectReference objRef, Field field) { final DescriptorData descriptorData; if (objRef == null ) { @@ -148,6 +152,7 @@ public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory { return getDescriptor(parent, descriptorData); } + @Override public LocalVariableDescriptorImpl getLocalVariableDescriptor(NodeDescriptor parent, LocalVariableProxy local) { return getDescriptor(parent, new LocalData((LocalVariableProxyImpl)local)); } @@ -156,7 +161,7 @@ public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory { return getDescriptor(parent, new ArgValueData(index, value, name)); } - public StackFrameDescriptorImpl getStackFrameDescriptor(NodeDescriptorImpl parent, StackFrameProxyImpl frameProxy) { + public StackFrameDescriptorImpl getStackFrameDescriptor(@Nullable NodeDescriptorImpl parent, @NotNull StackFrameProxyImpl frameProxy) { return getDescriptor(parent, new StackFrameData(frameProxy)); } @@ -184,6 +189,7 @@ public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory { return getDescriptor(parent, new ThreadGroupData(group)); } + @Override public UserExpressionDescriptor getUserExpressionDescriptor(NodeDescriptor parent, final DescriptorData data) { return getDescriptor(parent, data); } @@ -232,6 +238,7 @@ public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory { super(descriportTree); } + @Override protected NodeDescriptor getSearched(NodeDescriptor parent) { NodeDescriptor searched = super.getSearched(parent); if(searched == null) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/StackFrameDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/StackFrameDescriptorImpl.java index a883e647a525..6ea05b7db9db 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/StackFrameDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/StackFrameDescriptorImpl.java @@ -35,6 +35,7 @@ import com.intellij.util.ui.TextTransferable; import com.intellij.xdebugger.frame.XStackFrame; import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; import com.sun.jdi.*; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -58,8 +59,9 @@ public class StackFrameDescriptorImpl extends NodeDescriptorImpl implements Stac private Icon myIcon = AllIcons.Debugger.StackFrame; - public StackFrameDescriptorImpl(StackFrameProxyImpl frame, final MethodsTracker tracker) { + public StackFrameDescriptorImpl(@NotNull StackFrameProxyImpl frame, @NotNull MethodsTracker tracker) { myFrame = frame; + try { myUiIndex = frame.getFrameIndex(); myLocation = frame.location(); @@ -112,10 +114,12 @@ public class StackFrameDescriptorImpl extends NodeDescriptorImpl implements Stac } @Override + @NotNull public StackFrameProxyImpl getFrameProxy() { return myFrame; } + @NotNull @Override public DebugProcess getDebugProcess() { return myFrame.getVirtualMachine().getDebugProcess(); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java index ec762c3ace72..5e9c81732613 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java @@ -76,7 +76,8 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements myProject = project; } - public boolean isArray() { + @Override + public boolean isArray() { return myValue instanceof ArrayReference; } @@ -84,11 +85,13 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements return myIsDirty; } - public boolean isLvalue() { + @Override + public boolean isLvalue() { return myIsLvalue; } - public boolean isNull() { + @Override + public boolean isNull() { return myValue == null; } @@ -97,6 +100,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements return myValue instanceof StringReference; } + @Override public boolean isPrimitive() { return myValue instanceof PrimitiveValue; } @@ -113,6 +117,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements myShowIdLabel = showIdLabel; } + @Override public Value getValue() { // the following code makes sense only if we do not use ObjectReference.enableCollection() / disableCollection() // to keep temporary objects @@ -124,6 +129,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements final Semaphore semaphore = new Semaphore(); semaphore.down(); evalContext.getDebugProcess().getManagerThread().invoke(new SuspendContextCommandImpl(evalContext.getSuspendContext()) { + @Override public void contextAction() throws Exception { // re-setting the context will cause value recalculation try { @@ -134,6 +140,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements } } + @Override protected void commandCancelled() { semaphore.up(); } @@ -145,12 +152,14 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements return myValue; } + @Override public boolean isExpandable() { return myIsExpandable; } public abstract Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException; + @Override public final void setContext(EvaluationContextImpl evaluationContext) { DebuggerManagerThreadImpl.assertIsManagerThread(); myStoredEvaluationContext = evaluationContext; @@ -170,7 +179,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements myIsDirty = (value == null) ? myValue != null : !value.equals(myValue); } } - catch (ObjectCollectedException e) { + catch (ObjectCollectedException ignored) { myIsDirty = true; } } @@ -227,6 +236,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements return exceptionObj; } + @Override public void setAncestor(NodeDescriptor oldDescriptor) { super.setAncestor(oldDescriptor); myIsNew = false; @@ -237,6 +247,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements myIsLvalue = value; } + @Override protected String calcRepresentation(EvaluationContextImpl context, DescriptorLabelListener labelListener){ DebuggerManagerThreadImpl.assertIsManagerThread(); @@ -288,18 +299,21 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements } + @Override public String setValueLabel(String label) { final String customLabel = getCustomLabel(label); myValueLabel = customLabel; return setLabel(calcValueName() + " = " + customLabel); } + @Override public String setValueLabelFailed(EvaluateException e) { final String label = setFailed(e); setValueLabel(label); return label; } + @Override public Icon setValueIcon(Icon icon) { return myValueIcon = icon; } @@ -311,6 +325,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements public abstract String calcValueName(); + @Override public void displayAs(NodeDescriptor descriptor) { if (descriptor instanceof ValueDescriptorImpl) { ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)descriptor; @@ -370,6 +385,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements //use 'this' to reference parent node //for ex. FieldDescriptorImpl should return //this.fieldName + @Override public abstract PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException; public static String getIdLabel(ObjectReference objRef) { @@ -425,12 +441,14 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements } //Context is set to null + @Override public void clear() { super.clear(); setValueLabel(""); myIsExpandable = false; } + @Override @Nullable public ValueMarkup getMarkup(final DebugProcess debugProcess) { final Value value = getValue(); @@ -444,6 +462,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements return null; } + @Override public void setMarkup(final DebugProcess debugProcess, @Nullable final ValueMarkup markup) { final Value value = getValue(); if (value instanceof ObjectReference) { diff --git a/java/debugger/openapi/src/com/intellij/debugger/DebuggerContext.java b/java/debugger/openapi/src/com/intellij/debugger/DebuggerContext.java index d2c918061155..b9bf49b94af0 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/DebuggerContext.java +++ b/java/debugger/openapi/src/com/intellij/debugger/DebuggerContext.java @@ -18,8 +18,10 @@ package com.intellij.debugger; import com.intellij.debugger.engine.StackFrameContext; import com.intellij.debugger.engine.SuspendContext; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.Nullable; -public interface DebuggerContext extends StackFrameContext{ +public interface DebuggerContext extends StackFrameContext { + @Nullable SuspendContext getSuspendContext(); Project getProject(); diff --git a/java/debugger/openapi/src/com/intellij/debugger/PositionManagerFactory.java b/java/debugger/openapi/src/com/intellij/debugger/PositionManagerFactory.java index e89d423deef0..ff118b89c10c 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/PositionManagerFactory.java +++ b/java/debugger/openapi/src/com/intellij/debugger/PositionManagerFactory.java @@ -17,6 +17,7 @@ package com.intellij.debugger; import com.intellij.debugger.engine.DebugProcess; import com.intellij.openapi.extensions.ExtensionPointName; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -26,5 +27,5 @@ public abstract class PositionManagerFactory { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.debugger.positionManagerFactory"); @Nullable - public abstract PositionManager createPositionManager(DebugProcess process); + public abstract PositionManager createPositionManager(@NotNull DebugProcess process); } diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/StackFrameContext.java b/java/debugger/openapi/src/com/intellij/debugger/engine/StackFrameContext.java index 442f2c7c0a89..f4fe5ca9bcb7 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/engine/StackFrameContext.java +++ b/java/debugger/openapi/src/com/intellij/debugger/engine/StackFrameContext.java @@ -18,15 +18,10 @@ package com.intellij.debugger.engine; import com.intellij.debugger.engine.jdi.StackFrameProxy; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * User: lex - * Date: Jun 3, 2003 - * Time: 5:58:58 PM - * To change this template use Options | File Templates. - */ public interface StackFrameContext { @Nullable - StackFrameProxy getFrameProxy(); - DebugProcess getDebugProcess(); + StackFrameProxy getFrameProxy(); + + @Nullable + DebugProcess getDebugProcess(); } diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/SuspendContext.java b/java/debugger/openapi/src/com/intellij/debugger/engine/SuspendContext.java index ae89344dec63..be2c891d5fd0 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/engine/SuspendContext.java +++ b/java/debugger/openapi/src/com/intellij/debugger/engine/SuspendContext.java @@ -18,7 +18,7 @@ package com.intellij.debugger.engine; import com.intellij.debugger.engine.jdi.ThreadReferenceProxy; public interface SuspendContext extends StackFrameContext { - public int getSuspendPolicy(); + int getSuspendPolicy(); ThreadReferenceProxy getThread(); } diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.java index e50ef4380968..e991d0cd25fc 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.java +++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.java @@ -21,8 +21,10 @@ import com.intellij.debugger.engine.SuspendContext; import com.intellij.openapi.project.Project; import com.sun.jdi.ClassLoaderReference; import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; public interface EvaluationContext extends StackFrameContext{ + @NotNull DebugProcess getDebugProcess(); EvaluationContext createEvaluationContext(Value value); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java index a5104f1deb51..68e3554ce314 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java @@ -81,14 +81,15 @@ public class AddTypeCastFix extends LocalQuickFixAndIntentionActionOnPsiElement originalExpression.replace(typeCast); } - static PsiExpression createCastExpression(PsiExpression originalExpression, Project project, PsiType type) { + static PsiExpression createCastExpression(PsiExpression original, Project project, PsiType type) { // remove nested casts - PsiElement expression = PsiUtil.deparenthesizeExpression(originalExpression); + PsiElement expression = PsiUtil.deparenthesizeExpression(original); if (expression == null) return null; - PsiElementFactory factory = JavaPsiFacade.getInstance(originalExpression.getProject()).getElementFactory(); - PsiTypeCastExpression typeCast = (PsiTypeCastExpression)factory.createExpressionFromText("(Type)value", null); - assertNotNull(typeCast.getCastType()).replace(factory.createTypeElement(type)); + PsiElementFactory factory = JavaPsiFacade.getInstance(original.getProject()).getElementFactory(); + if (type instanceof PsiEllipsisType) type = ((PsiEllipsisType)type).toArrayType(); + String text = "(" + type.getCanonicalText(false) + ")value"; + PsiTypeCastExpression typeCast = (PsiTypeCastExpression)factory.createExpressionFromText(text, original); typeCast = (PsiTypeCastExpression)CodeStyleManager.getInstance(project).reformat(typeCast); if (expression instanceof PsiConditionalExpression) { @@ -124,5 +125,4 @@ public class AddTypeCastFix extends LocalQuickFixAndIntentionActionOnPsiElement public boolean startInWriteAction() { return true; } - } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java index 919fb4ea28db..b06ba2a5358b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java @@ -195,6 +195,13 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo PsiStatement body = foreachStatement.getBody(); final PsiExpression iteratedValue = foreachStatement.getIteratedValue(); if (body != null && iteratedValue != null) { + final Collection comments = PsiTreeUtil.findChildrenOfType(body, PsiComment.class); + + final PsiElement parent = foreachStatement.getParent(); + for (PsiElement comment : PsiTreeUtil.findChildrenOfType(body, PsiComment.class)) { + parent.addBefore(comment, foreachStatement); + } + final PsiParameter parameter = foreachStatement.getIterationParameter(); final PsiIfStatement ifStmt = extractIfStatement(body); @@ -294,13 +301,19 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo iteration += ").collect(java.util.stream.Collectors."; String variableName = null; - PsiExpression initializer = null; + PsiExpression primitiveInitializer = null; final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression(); if (qualifierExpression instanceof PsiReferenceExpression) { final PsiElement resolve = ((PsiReferenceExpression)qualifierExpression).resolve(); if (resolve instanceof PsiVariable) { if (resolve instanceof PsiLocalVariable && foreachStatement.equals(PsiTreeUtil.skipSiblingsForward(resolve.getParent(), PsiWhiteSpace.class))) { - initializer = ((PsiVariable)resolve).getInitializer(); + final PsiExpression initializer = ((PsiVariable)resolve).getInitializer(); + if (initializer instanceof PsiNewExpression) { + final PsiExpressionList argumentList = ((PsiNewExpression)initializer).getArgumentList(); + if (argumentList != null && argumentList.getExpressions().length == 0) { + primitiveInitializer = initializer; + } + } } variableName = ((PsiVariable)resolve).getName() + "."; } @@ -308,19 +321,26 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo variableName = ""; } + if (variableName != null) { + final PsiElement parent = foreachStatement.getParent(); + for (PsiElement comment : PsiTreeUtil.findChildrenOfType(body, PsiComment.class)) { + parent.addBefore(comment, foreachStatement); + } + } + PsiElement result = null; - if (initializer != null) { - final PsiType initializerType = initializer.getType(); + if (primitiveInitializer != null) { + final PsiType initializerType = primitiveInitializer.getType(); final PsiClassType rawType = initializerType instanceof PsiClassType ? ((PsiClassType)initializerType).rawType() : null; if (rawType != null && rawType.equalsToText(CommonClassNames.JAVA_UTIL_ARRAY_LIST)) { iteration += "toList()"; } else if (rawType != null && rawType.equalsToText(CommonClassNames.JAVA_UTIL_HASH_SET)) { iteration += "toSet()"; } else { - iteration += "toCollection(() -> " + initializer.getText() +")"; + iteration += "toCollection(() -> " + primitiveInitializer.getText() +")"; } iteration += ")"; - result = initializer.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(iteration, foreachStatement)); + result = primitiveInitializer.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(iteration, foreachStatement)); foreachStatement.delete(); } else if (variableName != null){ iteration += "toList())"; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToThrowsFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToThrowsFix.java index 7e70dbe8dd9c..13f9162f5f65 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToThrowsFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToThrowsFix.java @@ -57,8 +57,21 @@ public class AddExceptionToThrowsFix extends BaseIntentionAction { PsiDocumentManager.getInstance(project).commitAllDocuments(); - PsiMethod targetMethod = PsiTreeUtil.getParentOfType(myWrongElement, PsiMethod.class); - List exceptions = getUnhandledExceptions(myWrongElement, targetMethod); + PsiElement targetElement = null; + PsiMethod targetMethod = null; + + final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(myWrongElement, PsiLambdaExpression.class); + if (lambdaExpression != null) { + targetMethod = LambdaUtil.getFunctionalInterfaceMethod(lambdaExpression); + targetElement = lambdaExpression.getBody(); + } + + if (targetElement == null && targetMethod == null) { + targetMethod = PsiTreeUtil.getParentOfType(myWrongElement, PsiMethod.class); + targetElement = targetMethod; + } + + List exceptions = getUnhandledExceptions(myWrongElement, targetElement, targetMethod); if (exceptions == null || targetMethod == null) return; Set unhandledExceptions = new THashSet(exceptions); @@ -168,9 +181,21 @@ public class AddExceptionToThrowsFix extends BaseIntentionAction { if (!(file instanceof PsiJavaFile)) return false; if (myWrongElement == null || !myWrongElement.isValid()) return false; - PsiMethod method = PsiTreeUtil.getParentOfType(myWrongElement, PsiMethod.class); - if (method == null || !method.getThrowsList().isPhysical()) return false; - List unhandled = getUnhandledExceptions(myWrongElement, method); + PsiElement targetElement = null; + PsiMethod targetMethod = null; + + final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(myWrongElement, PsiLambdaExpression.class); + if (lambdaExpression != null) { + targetMethod = LambdaUtil.getFunctionalInterfaceMethod(lambdaExpression); + targetElement = lambdaExpression.getBody(); + } + + if (targetElement == null && targetMethod == null) { + targetMethod = PsiTreeUtil.getParentOfType(myWrongElement, PsiMethod.class); + targetElement = targetMethod; + } + if (targetElement == null || targetMethod == null || !targetMethod.getThrowsList().isPhysical()) return false; + List unhandled = getUnhandledExceptions(myWrongElement, targetElement, targetMethod); if (unhandled == null || unhandled.isEmpty()) return false; setText(QuickFixBundle.message("add.exception.to.throws.text", unhandled.size())); @@ -184,13 +209,13 @@ public class AddExceptionToThrowsFix extends BaseIntentionAction { } @Nullable - private static List getUnhandledExceptions(@Nullable PsiElement element, PsiMethod topElement) { + private static List getUnhandledExceptions(@Nullable PsiElement element, PsiElement topElement, PsiMethod targetMethod) { if (element == null || element == topElement) return null; List unhandledExceptions = ExceptionUtil.getUnhandledExceptions(element); - if (!filterInProjectExceptions(topElement, unhandledExceptions).isEmpty()) { + if (!filterInProjectExceptions(targetMethod, unhandledExceptions).isEmpty()) { return unhandledExceptions; } - return getUnhandledExceptions(element.getParent(), topElement); + return getUnhandledExceptions(element.getParent(), topElement, targetMethod); } @NotNull diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddOnDemandStaticImportAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddOnDemandStaticImportAction.java index a1d7988b9ed3..92700f6d8e86 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddOnDemandStaticImportAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddOnDemandStaticImportAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -18,7 +18,7 @@ package com.intellij.codeInsight.intention.impl; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.highlighting.HighlightManager; -import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.colors.EditorColors; @@ -39,7 +39,7 @@ import java.util.List; /** * @author ven */ -public class AddOnDemandStaticImportAction extends PsiElementBaseIntentionAction { +public class AddOnDemandStaticImportAction extends BaseElementAtCaretIntentionAction { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.AddOnDemandStaticImportAction"); @Override @@ -103,13 +103,16 @@ public class AddOnDemandStaticImportAction extends PsiElementBaseIntentionAction if (aClass == null) { return; } - PsiImportStaticStatement importStaticStatement = - JavaPsiFacade.getInstance(file.getProject()).getElementFactory().createImportStaticStatement(aClass, "*"); - PsiImportList importList = ((PsiJavaFile)file).getImportList(); - if (importList == null) { - return; + final PsiClass containingClass = PsiUtil.getTopLevelClass(refExpr); + if (aClass != containingClass) { + PsiImportStaticStatement importStaticStatement = + JavaPsiFacade.getInstance(file.getProject()).getElementFactory().createImportStaticStatement(aClass, "*"); + PsiImportList importList = ((PsiJavaFile)file).getImportList(); + if (importList == null) { + return; + } + importList.add(importStaticStatement); } - importList.add(importStaticStatement); List roots = file.getViewProvider().getAllFiles(); for (final PsiFile root : roots) { diff --git a/java/java-impl/src/com/intellij/codeInsight/template/impl/ShortenToStaticImportProcessor.java b/java/java-impl/src/com/intellij/codeInsight/template/impl/ShortenToStaticImportProcessor.java index 637ce3ede147..87a8f4c7155d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/impl/ShortenToStaticImportProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/impl/ShortenToStaticImportProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * 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. @@ -34,6 +34,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static java.util.Arrays.asList; @@ -72,6 +73,7 @@ public class ShortenToStaticImportProcessor implements TemplateOptionalProcessor } } + Collections.reverse(staticImportTargets); for (Pair pair : staticImportTargets) { if (pair.first.isValid()) { pair.second.perform(project, file, editor, pair.first); diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ExpressionPostfixTemplateWithChooser.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ExpressionPostfixTemplateWithChooser.java index 6f09a79e5b60..7efead36faa9 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ExpressionPostfixTemplateWithChooser.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ExpressionPostfixTemplateWithChooser.java @@ -1,3 +1,18 @@ +/* + * 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.codeInsight.template.postfix.templates; import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils; @@ -65,8 +80,14 @@ public abstract class ExpressionPostfixTemplateWithChooser extends PostfixTempla } @NotNull - protected List getExpressions(@NotNull PsiElement context, @NotNull Document document, int offset) { - List expressions = IntroduceVariableBase.collectExpressions(context.getContainingFile(), document, offset, false); + protected List getExpressions(@NotNull PsiElement context, @NotNull Document document, final int offset) { + List expressions = ContainerUtil.filter(IntroduceVariableBase.collectExpressions(context.getContainingFile(), document, offset - 1, false), + new Condition() { + @Override + public boolean value(PsiExpression expression) { + return expression.getTextRange().getEndOffset() == offset; + } + }); return ContainerUtil.filter(expressions.isEmpty() ? maybeTopmostExpression(context) : expressions, getTypeCondition()); } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index c39f35532d80..390116c3793c 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -1441,7 +1441,10 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { @Override public void visitTypeElement(PsiTypeElement type) { - if (myType2 == JavaTokenType.ELLIPSIS || myType2 == JavaTokenType.LBRACKET || myType2 == JavaTokenType.RBRACKET) { + if (myType1 == JavaElementType.ANNOTATION || myType2 == JavaElementType.ANNOTATION) { + createSpaceInCode(true); + } + else if (myType2 == JavaTokenType.ELLIPSIS || myType2 == JavaTokenType.LBRACKET || myType2 == JavaTokenType.RBRACKET) { createSpaceInCode(false); } else if (type.getType() instanceof PsiDisjunctionType) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FilePathReferenceProvider.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FilePathReferenceProvider.java index 4ca7694dfd5b..827a3d128a24 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FilePathReferenceProvider.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FilePathReferenceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -44,12 +44,12 @@ public class FilePathReferenceProvider extends PsiReferenceProvider { } @NotNull - public PsiReference[] getReferencesByElement(PsiElement element, String text, int offset, final boolean soft) { + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, String text, int offset, final boolean soft) { return getReferencesByElement(element, text, offset, soft, Module.EMPTY_ARRAY); } @NotNull - public PsiReference[] getReferencesByElement(PsiElement element, + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, String text, int offset, final boolean soft, diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PathListReferenceProvider.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PathListReferenceProvider.java index 3685ef7c9b8b..df171339ff01 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PathListReferenceProvider.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PathListReferenceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * 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. @@ -75,7 +75,7 @@ public class PathListReferenceProvider extends PsiReferenceProvider { return ','; } - protected PsiReference[] createReferences(PsiElement element, String s, int offset, final boolean soft) { + protected PsiReference[] createReferences(@NotNull PsiElement element, String s, int offset, final boolean soft) { int contentOffset = StringUtil.findFirst(s, CharFilter.NOT_WHITESPACE_FILTER); if (contentOffset >= 0) { offset += contentOffset; diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index 54bd0241e64e..e1a9469765d3 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -33,6 +33,7 @@ import java.util.*; * Date: 7/17/12 */ public class LambdaUtil { + public static ThreadLocal> ourFunctionTypes = new ThreadLocal>(); private static final Logger LOG = Logger.getInstance("#" + LambdaUtil.class.getName()); @NonNls public static final String JAVA_LANG_FUNCTIONAL_INTERFACE = "java.lang.FunctionalInterface"; @@ -318,6 +319,13 @@ public class LambdaUtil { final int finalLambdaIdx = adjustLambdaIdx(lambdaIdx, (PsiMethod)resolve, parameters); if (finalLambdaIdx < parameters.length) { if (!tryToSubstitute) return getNormalizedType(parameters[finalLambdaIdx]); + final Map map = ourFunctionTypes.get(); + if (map != null) { + final PsiType type = map.get(expression); + if (type != null) { + return type; + } + } return PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, true, new Computable() { @Override public PsiType compute() { diff --git a/java/java-psi-api/src/com/intellij/psi/PsiNameHelper.java b/java/java-psi-api/src/com/intellij/psi/PsiNameHelper.java index 6e5e6563dc3b..9b7252714cd1 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiNameHelper.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiNameHelper.java @@ -58,7 +58,7 @@ public abstract class PsiNameHelper { * @param languageLevel to check text against. For instance 'assert' or 'enum' might or might not be identifiers depending on language level * @return true if the text is an identifier, false otherwise */ - public abstract boolean isIdentifier(@Nullable String text, LanguageLevel languageLevel); + public abstract boolean isIdentifier(@Nullable String text, @NotNull LanguageLevel languageLevel); /** * Checks if the specified text is a Java keyword, using the language level of the project @@ -80,7 +80,9 @@ public abstract class PsiNameHelper { @NotNull public static String getShortClassName(@NotNull String referenceText) { - int lessPos = referenceText.length(), bracesBalance = 0, i; + int lessPos = referenceText.length(); + int bracesBalance = 0; + int i; loop: for (i = referenceText.length() - 1; i >= 0; i--) { @@ -137,7 +139,7 @@ public abstract class PsiNameHelper { } @NotNull - public static String getQualifiedClassName(String referenceText, boolean removeWhitespace) { + public static String getQualifiedClassName(@NotNull String referenceText, boolean removeWhitespace) { if (removeWhitespace) { referenceText = removeWhitespace(referenceText); } @@ -166,7 +168,7 @@ public abstract class PsiNameHelper { } private static final Pattern WHITESPACE_PATTERN = Pattern.compile("(?:\\s)|(?:/\\*.*\\*/)|(?://[^\\n]*)"); - private static String removeWhitespace(String referenceText) { + private static String removeWhitespace(@NotNull String referenceText) { return WHITESPACE_PATTERN.matcher(referenceText).replaceAll(""); } @@ -178,7 +180,8 @@ public abstract class PsiNameHelper { * @param referenceText the text of the reference to calculate type parameters for. * @return the calculated array of type parameters. */ - public static String[] getClassParametersText(String referenceText) { + @NotNull + public static String[] getClassParametersText(@NotNull String referenceText) { if (referenceText.indexOf('<') < 0) return ArrayUtil.EMPTY_STRING_ARRAY; referenceText = removeWhitespace(referenceText); final char[] chars = referenceText.toCharArray(); @@ -287,6 +290,6 @@ public abstract class PsiNameHelper { } sb.append(' '); } - return annotations.size() > 0; + return !annotations.isEmpty(); } } diff --git a/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java b/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java index 2afc76a5d060..2831025839e3 100644 --- a/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java +++ b/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java @@ -82,9 +82,11 @@ public class MethodCandidateInfo extends CandidateInfo{ @ApplicabilityLevelConstant private int getApplicabilityLevelInner() { - if (myArgumentTypes == null) return ApplicabilityLevel.NOT_APPLICABLE; + final PsiType[] argumentTypes = getArgumentTypes(); - int level = PsiUtil.getApplicabilityLevel(getElement(), getSubstitutor(), myArgumentTypes, myLanguageLevel); + if (argumentTypes == null) return ApplicabilityLevel.NOT_APPLICABLE; + + int level = PsiUtil.getApplicabilityLevel(getElement(), getSubstitutor(), argumentTypes, myLanguageLevel); if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable()) level = ApplicabilityLevel.NOT_APPLICABLE; return level; } @@ -104,18 +106,17 @@ public class MethodCandidateInfo extends CandidateInfo{ return getApplicabilityLevel(); } - if (myArgumentTypes == null) { - return ApplicabilityLevel.NOT_APPLICABLE; - } - @ApplicabilityLevelConstant int level; Integer boxedLevel = ourOverloadGuard.doPreventingRecursion(myArgumentList, false, new Computable() { @Override public Integer compute() { - - final PsiMethod method = getElement(); - if (method != null && method.hasTypeParameters() || myArgumentList == null || !PsiUtil.isLanguageLevel8OrHigher(myArgumentList)) { - return PsiUtil.getApplicabilityLevel(getElement(), getSubstitutor(false), myArgumentTypes, myLanguageLevel); + if (PsiUtil.isLanguageLevel8OrHigher(myArgumentList)) { + final PsiType[] argumentTypes = getArgumentTypes(); + if (argumentTypes == null) { + return ApplicabilityLevel.NOT_APPLICABLE; + } + + return PsiUtil.getApplicabilityLevel(getElement(), getSubstitutor(false), argumentTypes, myLanguageLevel); } return getApplicabilityLevelInner(); } @@ -127,7 +128,17 @@ public class MethodCandidateInfo extends CandidateInfo{ } public PsiSubstitutor getSiteSubstitutor() { - return super.getSubstitutor(); + PsiSubstitutor incompleteSubstitutor = super.getSubstitutor(); + if (myTypeArguments != null) { + PsiMethod method = getElement(); + if (method != null) { + PsiTypeParameter[] typeParams = method.getTypeParameters(); + for (int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) { + incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]); + } + } + } + return incompleteSubstitutor; } @NotNull @@ -206,22 +217,10 @@ public class MethodCandidateInfo extends CandidateInfo{ return inferTypeArguments(policy, arguments, true); } else { - return typeArgumentsSubstitutor(); + return getSiteSubstitutor(); } } - public PsiSubstitutor typeArgumentsSubstitutor() { - PsiSubstitutor incompleteSubstitutor = super.getSubstitutor(); - PsiMethod method = getElement(); - if (method != null) { - PsiTypeParameter[] typeParams = method.getTypeParameters(); - for (int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) { - incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]); - } - } - return incompleteSubstitutor; - } - @NotNull public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, @NotNull PsiExpression[] arguments, @@ -283,6 +282,10 @@ public class MethodCandidateInfo extends CandidateInfo{ } } + public PsiType[] getArgumentTypes() { + return myArgumentTypes; + } + public static class ApplicabilityLevel { public static final int NOT_APPLICABLE = 1; public static final int VARARGS = 2; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiNameHelperImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiNameHelperImpl.java index 702ed8e36e20..cc99abe4ca97 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiNameHelperImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiNameHelperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -36,6 +36,7 @@ public class PsiNameHelperImpl extends PsiNameHelper { return isIdentifier(text, getLanguageLevel()); } + @NotNull protected LanguageLevel getLanguageLevel() { return myLanguageLevelExtension.getLanguageLevel(); } @@ -65,6 +66,7 @@ public class PsiNameHelperImpl extends PsiNameHelper { public static PsiNameHelper getInstance() { return new PsiNameHelperImpl() { + @NotNull @Override protected LanguageLevel getLanguageLevel() { return LanguageLevel.HIGHEST; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java index f30c7ae6382c..9766122e31d0 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java @@ -74,7 +74,9 @@ public class InferenceIncorporationPhase { } public boolean incorporate() { - for (InferenceVariable inferenceVariable : mySession.getInferenceVariables()) { + final Collection inferenceVariables = mySession.getInferenceVariables(); + final PsiSubstitutor substitutor = mySession.retrieveNonPrimitiveEqualsBounds(inferenceVariables); + for (InferenceVariable inferenceVariable : inferenceVariables) { if (inferenceVariable.getInstantiation() != PsiType.NULL) continue; final List eqBounds = inferenceVariable.getBounds(InferenceBound.EQ); final List upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER); @@ -82,15 +84,14 @@ public class InferenceIncorporationPhase { eqEq(eqBounds); - upDown(lowerBounds, upperBounds); - upDown(eqBounds, upperBounds); - upDown(lowerBounds, eqBounds); + upDown(lowerBounds, upperBounds, substitutor); + upDown(eqBounds, upperBounds, substitutor); + upDown(lowerBounds, eqBounds, substitutor); upUp(upperBounds); for (PsiType eqBound : eqBounds) { if (mySession.isProperType(eqBound)) { - final PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.put(inferenceVariable.getParameter(), eqBound); for (PsiType upperBound : upperBounds) { if (!mySession.isProperType(upperBound)) { addConstraint(new StrictSubtypingConstraint(substitutor.substitute(upperBound), eqBound)); @@ -274,12 +275,12 @@ public class InferenceIncorporationPhase { * or * S <: a & a <: T imply S <: T */ - private void upDown(List eqBounds, List upperBounds) { + private void upDown(List eqBounds, List upperBounds, PsiSubstitutor substitutor) { for (PsiType upperBound : upperBounds) { if (upperBound == null) continue; for (PsiType eqBound : eqBounds) { if (eqBound == null) continue; - addConstraint(new StrictSubtypingConstraint(upperBound, eqBound)); + addConstraint(new StrictSubtypingConstraint(substitutor.substitute(upperBound), substitutor.substitute(eqBound))); } } } @@ -289,7 +290,7 @@ public class InferenceIncorporationPhase { */ private void eqEq(List eqBounds) { for (int i = 0; i < eqBounds.size(); i++) { - PsiType sBound= eqBounds.get(i); + PsiType sBound = eqBounds.get(i); for (int j = i + 1; j < eqBounds.size(); j++) { final PsiType tBound = eqBounds.get(j); addConstraint(new TypeEqualityConstraint(tBound, sBound)); 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 53911011bc17..c2fe04bad07d 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 @@ -823,15 +823,19 @@ public class InferenceSession { MethodCandidateInfo.updateSubstitutor(argumentList, substitutor); } - for (ConstraintFormula additionalConstraint : subset) { - additionalConstraint.apply(substitutor); - } + try { + for (ConstraintFormula additionalConstraint : subset) { + additionalConstraint.apply(substitutor); + } - myConstraints.addAll(subset); - if (!repeatInferencePhases(true)) { - return false; + myConstraints.addAll(subset); + if (!repeatInferencePhases(true)) { + return false; + } + } + finally { + LambdaUtil.ourFunctionTypes.set(null); } - } return true; } @@ -995,8 +999,9 @@ public class InferenceSession { return true; } - if (sReturnType == PsiType.VOID && session != null) { - return false; + final List returnExpressions = LambdaUtil.getReturnExpressions((PsiLambdaExpression)arg); + if (sReturnType == PsiType.VOID) { + return returnExpressions.isEmpty() && session == null; } if (LambdaUtil.isFunctionalType(sReturnType) && LambdaUtil.isFunctionalType(tReturnType) && @@ -1005,15 +1010,14 @@ public class InferenceSession { //Otherwise, if R1 and R2 are functional interface types, and neither interface is a subinterface of the other, //then these rules are applied recursively to R1 and R2, for each result expression in expi. - final List returnExpressions = LambdaUtil.getReturnExpressions((PsiLambdaExpression)arg); if (!isFunctionalTypeMoreSpecific(sReturnType, tReturnType, session, returnExpressions.toArray(new PsiExpression[returnExpressions.size()]))) { return false; } } else { - final boolean sPrimitive = sReturnType instanceof PsiPrimitiveType; - final boolean tPrimitive = tReturnType instanceof PsiPrimitiveType; + final boolean sPrimitive = sReturnType instanceof PsiPrimitiveType && sReturnType != PsiType.VOID; + final boolean tPrimitive = tReturnType instanceof PsiPrimitiveType && tReturnType != PsiType.VOID; if (sPrimitive ^ tPrimitive) { - for (PsiExpression returnExpression : LambdaUtil.getReturnExpressions((PsiLambdaExpression)arg)) { + for (PsiExpression returnExpression : returnExpressions) { if (!PsiPolyExpressionUtil.isPolyExpression(returnExpression)) { final PsiType returnExpressionType = returnExpression.getType(); if (sPrimitive) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java index 0a49428722a1..fbd04e531e22 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java @@ -22,9 +22,7 @@ import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.Nullable; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * User: anna @@ -110,5 +108,11 @@ public abstract class InputOutputConstraintFormula implements ConstraintFormula @Override public void apply(PsiSubstitutor substitutor) { setT(substitutor.substitute(getT())); + Map map = LambdaUtil.ourFunctionTypes.get(); + if (map == null) { + map = new HashMap(); + LambdaUtil.ourFunctionTypes.set(map); + } + map.put(getExpression(), getT()); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java index 084be88d7428..4e983e1334a8 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java @@ -161,7 +161,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase if (containingClass != null) { PsiMethod[] methods = null; if (element instanceof PsiIdentifier) { - methods = containingClass.findMethodsByName(element.getText(), false); + methods = containingClass.findMethodsByName(element.getText(), true); } else if (isConstructor()) { final PsiElementFactory factory = JavaPsiFacade.getElementFactory(getProject()); diff --git a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java index 406abbf9ee7a..9a2504c5ad2b 100644 --- a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java +++ b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java @@ -72,7 +72,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ if (conflicts.isEmpty()) return null; if (conflicts.size() == 1) return conflicts.get(0); - boolean atLeastOneMatch = checkParametersNumber(conflicts, getActualParameterTypes().length, true); + boolean atLeastOneMatch = checkParametersNumber(conflicts, getActualParametersLength(), true); if (conflicts.size() == 1) return conflicts.get(0); checkSameSignatures(conflicts); @@ -81,7 +81,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ checkAccessStaticLevels(conflicts, true); if (conflicts.size() == 1) return conflicts.get(0); - checkParametersNumber(conflicts, getActualParameterTypes().length, false); + checkParametersNumber(conflicts, getActualParametersLength(), false); if (conflicts.size() == 1) return conflicts.get(0); final int applicabilityLevel = checkApplicability(conflicts); @@ -97,7 +97,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ checkSpecifics(conflicts, applicabilityLevel, myLanguageLevel); if (conflicts.size() == 1) return conflicts.get(0); - checkPrimitiveVarargs(conflicts, getActualParameterTypes().length); + checkPrimitiveVarargs(conflicts, getActualParametersLength()); if (conflicts.size() == 1) return conflicts.get(0); checkAccessStaticLevels(conflicts, false); @@ -110,23 +110,51 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ private void checkLambdaApplicable(@NotNull List conflicts, @NotNull LanguageLevel languageLevel) { if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) return; - for (int i = 0; i < getActualParameterTypes().length; i++) { - PsiType parameterType = getActualParameterTypes()[i]; - if (parameterType instanceof PsiLambdaExpressionType) { - final PsiLambdaExpression lambdaExpression = ((PsiLambdaExpressionType)parameterType).getExpression(); - for (Iterator iterator = conflicts.iterator(); iterator.hasNext(); ) { - ProgressManager.checkCanceled(); - final CandidateInfo conflict = iterator.next(); - final PsiMethod method = (PsiMethod)conflict.getElement(); - if (method != null) { - final PsiParameter[] methodParameters = method.getParameterList().getParameters(); - if (methodParameters.length == 0) continue; - final PsiParameter param = i < methodParameters.length ? methodParameters[i] : methodParameters[methodParameters.length - 1]; - final PsiType paramType = param.getType(); - if (!lambdaExpression.isAcceptable(((MethodCandidateInfo)conflict).getSubstitutor(false).substitute(paramType), lambdaExpression.hasFormalParameterTypes())) { - iterator.remove(); - } - } + for (int i = 0; i < getActualParametersLength(); i++) { + + PsiExpression expression; + if (myArgumentsList instanceof PsiExpressionList) { + expression = ((PsiExpressionList)myArgumentsList).getExpressions()[i]; + } + else { + final PsiType argType = getActualParameterTypes()[i]; + expression = argType instanceof PsiLambdaExpressionType ? ((PsiLambdaExpressionType)argType).getExpression() : null; + } + + final PsiLambdaExpression lambdaExpression = findNestedLambdaExpression(expression); + if (lambdaExpression != null) { + checkLambdaApplicable(conflicts, i, lambdaExpression); + } + } + } + + private static PsiLambdaExpression findNestedLambdaExpression(PsiExpression expression) { + if (expression instanceof PsiLambdaExpression) { + return (PsiLambdaExpression)expression; + } else if (expression instanceof PsiParenthesizedExpression) { + return findNestedLambdaExpression(((PsiParenthesizedExpression)expression).getExpression()); + } else if (expression instanceof PsiConditionalExpression) { + PsiLambdaExpression lambdaExpression = findNestedLambdaExpression(((PsiConditionalExpression)expression).getThenExpression()); + if (lambdaExpression != null) { + return lambdaExpression; + } + return findNestedLambdaExpression(((PsiConditionalExpression)expression).getElseExpression()); + } + return null; + } + + private static void checkLambdaApplicable(List conflicts, int i, PsiLambdaExpression lambdaExpression) { + for (Iterator iterator = conflicts.iterator(); iterator.hasNext(); ) { + ProgressManager.checkCanceled(); + final CandidateInfo conflict = iterator.next(); + final PsiMethod method = (PsiMethod)conflict.getElement(); + if (method != null) { + final PsiParameter[] methodParameters = method.getParameterList().getParameters(); + if (methodParameters.length == 0) continue; + final PsiParameter param = i < methodParameters.length ? methodParameters[i] : methodParameters[methodParameters.length - 1]; + final PsiType paramType = param.getType(); + if (!lambdaExpression.isAcceptable(((MethodCandidateInfo)conflict).getSubstitutor(false).substitute(paramType), lambdaExpression.hasFormalParameterTypes())) { + iterator.remove(); } } } @@ -411,6 +439,14 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ return myActualParameterTypes; } + private int getActualParametersLength() { + if (myActualParameterTypes == null) { + LOG.assertTrue(myArgumentsList instanceof PsiExpressionList, myArgumentsList); + return ((PsiExpressionList)myArgumentsList).getExpressions().length; + } + return myActualParameterTypes.length; + } + protected PsiType[] getArgumentTypes() { return ((PsiExpressionList)myArgumentsList).getExpressionTypes(); } @@ -500,8 +536,8 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ final PsiSubstitutor siteSubstitutor1 = info1.getSiteSubstitutor(); final PsiSubstitutor siteSubstitutor2 = info2.getSiteSubstitutor(); - final PsiType[] types2AtSite = typesAtSite(types2, siteSubstitutor2, typeParameters2); - final PsiType[] types1AtSite = typesAtSite(types1, siteSubstitutor1, typeParameters1); + final PsiType[] types2AtSite = typesAtSite(types2, siteSubstitutor2); + final PsiType[] types1AtSite = typesAtSite(types1, siteSubstitutor1); final PsiSubstitutor methodSubstitutor1 = calculateMethodSubstitutor(typeParameters1, method1, siteSubstitutor1, types1, types2AtSite, languageLevel); @@ -538,7 +574,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && myArgumentsList instanceof PsiExpressionList && (typeParameters1.length == 0 || typeParameters2.length == 0)) { boolean toCompareFunctional = false; - for (int i = 0; i < myActualParameterTypes.length; i++) { + for (int i = 0; i < getActualParametersLength(); i++) { if (types1.length > 0 && LambdaUtil.isFunctionalType(types1[Math.min(i, types1.length - 1)]) && types2.length > 0 && LambdaUtil.isFunctionalType(types2[Math.min(i, types2.length - 1)])) { types1AtSite[Math.min(i, types1.length - 1)] = PsiType.NULL; @@ -555,7 +591,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ if (applicable12ignoreFunctionalType || applicable21ignoreFunctionalType) { Specifics specifics = null; - for (int i = 0; i < myActualParameterTypes.length; i++) { + for (int i = 0; i < getActualParametersLength(); i++) { if (types1.length > 0 && types1AtSite[Math.min(i, types1.length - 1)] == PsiType.NULL && types2.length > 0 && types2AtSite[Math.min(i, types2.length - 1)] == PsiType.NULL) { Specifics specific = isFunctionalTypeMoreSpecific(info1, info2, ((PsiExpressionList)myArgumentsList).getExpressions()[i], i); @@ -642,7 +678,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ return applicabilityLevel > MethodCandidateInfo.ApplicabilityLevel.NOT_APPLICABLE; } - private static PsiType[] typesAtSite(PsiType[] types1, PsiSubstitutor siteSubstitutor1, PsiTypeParameter[] typeParameters1) { + private static PsiType[] typesAtSite(PsiType[] types1, PsiSubstitutor siteSubstitutor1) { final PsiType[] types = PsiType.createArray(types1.length); for (int i = 0; i < types1.length; i++) { types[i] = siteSubstitutor1.substitute(types1[i]); diff --git a/java/java-psi-impl/src/com/intellij/psi/scope/processor/MethodCandidatesProcessor.java b/java/java-psi-impl/src/com/intellij/psi/scope/processor/MethodCandidatesProcessor.java index ad0e7358842c..8607ff43c60e 100644 --- a/java/java-psi-impl/src/com/intellij/psi/scope/processor/MethodCandidatesProcessor.java +++ b/java/java-psi-impl/src/com/intellij/psi/scope/processor/MethodCandidatesProcessor.java @@ -63,7 +63,12 @@ public class MethodCandidatesProcessor extends MethodsProcessor{ final boolean staticProblem, final boolean accessible) { final PsiExpressionList argumentList = getArgumentList(); return new MethodCandidateInfo(method, substitutor, !accessible, staticProblem, argumentList, myCurrentFileContext, - getExpressionTypes(argumentList), getTypeArguments(), getLanguageLevel()); + null, getTypeArguments(), getLanguageLevel()) { + @Override + public PsiType[] getArgumentTypes() { + return getExpressionTypes(argumentList); + } + }; } protected PsiType[] getExpressionTypes(PsiExpressionList argumentList) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/IncorporationWithEqualsBoundsSubstitution.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/IncorporationWithEqualsBoundsSubstitution.java new file mode 100644 index 000000000000..5f85bc0d79eb --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/IncorporationWithEqualsBoundsSubstitution.java @@ -0,0 +1,14 @@ +abstract class Test { + abstract Tf foo(Class c); + abstract Tf1 foo1(Class c); + + abstract Class bar(Class clazz); + abstract Class bar1(Class clazz); + + { + foo(bar(String.class)); + foo(bar1(String.class)); + foo1(bar(String.class)); + foo1(bar1(String.class)); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java index 6c664dd9e65a..595787704437 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java @@ -22,6 +22,6 @@ class AmbiguityRawGenerics { void foo(I3 s) { } void bar() { - foo(()-> { throw new RuntimeException(); }); + foo(()-> { throw new RuntimeException(); }); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/NestedLambdaSpecifics.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/NestedLambdaSpecifics.java new file mode 100644 index 000000000000..be47ffbc83b8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/NestedLambdaSpecifics.java @@ -0,0 +1,19 @@ +class Test { + + interface A { + T a(); + } + + interface B { + T b(); + } + + private void m(A a) { } + private void m(B b) { } + + { + m((() -> 42)); + m(true ? () -> 42 : () -> 42); + m(true ? null : (() -> 42)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA118965comment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA118965comment.java new file mode 100644 index 000000000000..501e5f8d51cc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA118965comment.java @@ -0,0 +1,13 @@ +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class IDEA118965 { + { + Stream words = Arrays.asList("one", "one", "two").stream(); + List> res = words.collect(Collectors.toMap(w -> w, w -> 1, (a, b) -> a + b)) + .entrySet().stream().filter(e -> e.getValue() > 1).collect(Collectors.toList()); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA121315.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA121315.java new file mode 100644 index 000000000000..1c22302f2523 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA121315.java @@ -0,0 +1,22 @@ +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class IDEA121315 { + class Issue { + Long getId() { + return 1l; + } + } + + T id(T i) { + return i; + } + + void foo(Stream map){ + Map id2Issue = map.collect(Collectors.toMap(null, p -> id(p))); + Map id2Issue1 = map.collect(Collectors.toMap(null, p -> p)); + Map id2Issue2 = map.collect(Collectors.toMap(null, this::id)); + + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/SuperClassPotentiallyApplicableMembers.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/SuperClassPotentiallyApplicableMembers.java new file mode 100644 index 000000000000..d9f05fb8a075 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/SuperClassPotentiallyApplicableMembers.java @@ -0,0 +1,39 @@ +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +interface Base { + int getInt(String a); + int getInt(); + + void foo(); +} + +abstract class ABase implements Base { + @Override + public int getInt(String a) { + return 0; + } + + @Override + public int getInt() { + return 0; + } +} + +class Impl extends ABase { + @Override + public int getInt() { + return 0; + } + + @Override + public void foo() { + List strs = Arrays.asList("one", "two"); + + List withMethodRef = strs + .stream() + .map(this::getInt) + .collect( Collectors.toList()); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addOnDemandStaticImport/afterOwnMember.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addOnDemandStaticImport/afterOwnMember.java new file mode 100644 index 000000000000..926689ce7891 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addOnDemandStaticImport/afterOwnMember.java @@ -0,0 +1,10 @@ +// "Add on demand static import for 'test.Foo'" "true" +package test; + +class Foo { + public static void m() {} + + public static void main(String[] args) { + m(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addOnDemandStaticImport/beforeOwnMember.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addOnDemandStaticImport/beforeOwnMember.java new file mode 100644 index 000000000000..fba407683654 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addOnDemandStaticImport/beforeOwnMember.java @@ -0,0 +1,10 @@ +// "Add on demand static import for 'test.Foo'" "true" +package test; + +class Foo { + public static void m() {} + + public static void main(String[] args) { + Foo.m(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addToThrows/afterLambdaInternal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addToThrows/afterLambdaInternal.java new file mode 100644 index 000000000000..056c098fcf7e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addToThrows/afterLambdaInternal.java @@ -0,0 +1,12 @@ +// "Add Exception to Method Signature" "true" +class C { + interface I { + void a() throws InterruptedException; + } + + { + I i = () -> { + Thread.sleep(2000); + }; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addToThrows/beforeLambdaExternal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addToThrows/beforeLambdaExternal.java new file mode 100644 index 000000000000..5aac457b22ef --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addToThrows/beforeLambdaExternal.java @@ -0,0 +1,9 @@ +// "Add Exception to Method Signature" "false" +class C { + + public static void main(String[] args) throws InterruptedException { + new Thread(( ) -> { + Thread.sleep(2000); + }).start(); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addToThrows/beforeLambdaInternal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addToThrows/beforeLambdaInternal.java new file mode 100644 index 000000000000..9c62d2b6cff1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addToThrows/beforeLambdaInternal.java @@ -0,0 +1,12 @@ +// "Add Exception to Method Signature" "true" +class C { + interface I { + void a(); + } + + { + I i = () -> { + Thread.sleep(2000); + }; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoArray.java index 659a3bb0b844..dc9c06ca5329 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoArray.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoArray.java @@ -7,6 +7,6 @@ import static java.lang.annotation.ElementType.*; class C { { Object o = null; - @TA int @TA [] a = (@TA int @TA[]) o; + @TA int @TA [] a = (int[]) o; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoPrimitive.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoPrimitive.java deleted file mode 100644 index 8cd2a01d46e8..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoPrimitive.java +++ /dev/null @@ -1,12 +0,0 @@ -// "Cast to 'int'" "true" -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; - -@Target({TYPE_USE}) @interface TA { } - -class C { - { - Object o = null; - @TA int i = (@TA int) o; - } -} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoRefType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoRefType.java index 28420d1f46ea..d1b92436866a 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoRefType.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoRefType.java @@ -1,5 +1,6 @@ -// "Cast to 'java.lang.Integer'" "true" +// "Cast to 'java.util.List'" "true" import java.lang.annotation.*; +import java.util.*; import static java.lang.annotation.ElementType.*; @Target({TYPE_USE}) @interface TA { } @@ -7,6 +8,6 @@ import static java.lang.annotation.ElementType.*; class C { { Object o = null; - @TA Integer i = (@TA Integer) o; + @TA List<@TA Integer> i = (List) o; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoTypeParam.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoTypeParam.java deleted file mode 100644 index 6fa421e10993..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoTypeParam.java +++ /dev/null @@ -1,13 +0,0 @@ -// "Cast to 'java.util.List'" "true" -import java.lang.annotation.*; -import java.util.List; -import static java.lang.annotation.ElementType.*; - -@Target({TYPE_USE}) @interface TA { } - -class C { - { - Object o = null; - @TA List<@TA String> l = (@TA List<@TA String>) o; - } -} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoValued.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoValued.java deleted file mode 100644 index bfedf89bc35f..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoValued.java +++ /dev/null @@ -1,12 +0,0 @@ -// "Cast to 'int'" "true" -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; - -@Target({TYPE_USE}) @interface TA { String value() default ""; } - -class C { - { - Object o = null; - @TA("wtf") int i = (@TA("wtf") int) o; - } -} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoWildcard.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoWildcard.java deleted file mode 100644 index a114746c145d..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTypeAnnoWildcard.java +++ /dev/null @@ -1,13 +0,0 @@ -// "Cast to 'java.util.List'" "true" -import java.lang.annotation.*; -import java.util.List; -import static java.lang.annotation.ElementType.*; - -@Target({TYPE_USE}) @interface TA { } - -class C { - { - Object o = null; - @TA List<@TA ? extends @TA String> l = (@TA List<@TA ? extends @TA String>) o; - } -} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoPrimitive.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoPrimitive.java deleted file mode 100644 index 5d17c26ca97a..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoPrimitive.java +++ /dev/null @@ -1,12 +0,0 @@ -// "Cast to 'int'" "true" -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; - -@Target({TYPE_USE}) @interface TA { } - -class C { - { - Object o = null; - @TA int i = o; - } -} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoRefType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoRefType.java index 7948738bb264..7c95d5eae896 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoRefType.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoRefType.java @@ -1,5 +1,6 @@ -// "Cast to 'java.lang.Integer'" "true" +// "Cast to 'java.util.List'" "true" import java.lang.annotation.*; +import java.util.*; import static java.lang.annotation.ElementType.*; @Target({TYPE_USE}) @interface TA { } @@ -7,6 +8,6 @@ import static java.lang.annotation.ElementType.*; class C { { Object o = null; - @TA Integer i = o; + @TA List<@TA Integer> i = o; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoTypeParam.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoTypeParam.java deleted file mode 100644 index 33b2d0be44da..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoTypeParam.java +++ /dev/null @@ -1,13 +0,0 @@ -// "Cast to 'java.util.List'" "true" -import java.lang.annotation.*; -import java.util.List; -import static java.lang.annotation.ElementType.*; - -@Target({TYPE_USE}) @interface TA { } - -class C { - { - Object o = null; - @TA List<@TA String> l = o; - } -} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoValued.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoValued.java deleted file mode 100644 index 726856c931b7..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoValued.java +++ /dev/null @@ -1,12 +0,0 @@ -// "Cast to 'int'" "true" -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; - -@Target({TYPE_USE}) @interface TA { String value() default ""; } - -class C { - { - Object o = null; - @TA("wtf") int i = o; - } -} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoWildcard.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoWildcard.java deleted file mode 100644 index 84a9df6fad0a..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/beforeTypeAnnoWildcard.java +++ /dev/null @@ -1,13 +0,0 @@ -// "Cast to 'java.util.List'" "true" -import java.lang.annotation.*; -import java.util.List; -import static java.lang.annotation.ElementType.*; - -@Target({TYPE_USE}) @interface TA { } - -class C { - { - Object o = null; - @TA List<@TA ? extends @TA String> l = o; - } -} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectArrayListComment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectArrayListComment.java new file mode 100644 index 000000000000..a9b24a5a2005 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectArrayListComment.java @@ -0,0 +1,16 @@ +// "Replace with collect" "true" +import java.util.*; +import java.util.stream.Collectors; + +public class Collect { + class Person { + String getName() { + return ""; + } + } + + void collectNames(List persons){ + List names = persons.stream().map(Person::getName).collect(Collectors.toList()); + //some comment + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectArrayListNonTrivialInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectArrayListNonTrivialInitializer.java new file mode 100644 index 000000000000..641617eb642b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectArrayListNonTrivialInitializer.java @@ -0,0 +1,20 @@ +// "Replace with collect" "true" +import java.util.*; +import java.util.stream.Collectors; + +public class Collect { + class Person { + String getName() { + return ""; + } + } + + ArrayList foo() { + return new ArrayList<>(); + } + + void collectNames(List persons){ + List names = foo(); + names.addAll(persons.stream().map(Person::getName).collect(Collectors.toList())); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterNormalComments.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterNormalComments.java new file mode 100644 index 000000000000..c1bbf0a0924b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterNormalComments.java @@ -0,0 +1,11 @@ +// "Replace with forEach" "true" +import java.util.ArrayList; +import java.util.List; + +class Sample { + List foo = new ArrayList<>(); + { + //some comment + foo.forEach(System.out::println); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectArrayListComment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectArrayListComment.java new file mode 100644 index 000000000000..c26fa6954bd6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectArrayListComment.java @@ -0,0 +1,18 @@ +// "Replace with collect" "true" +import java.util.*; + +public class Collect { + class Person { + String getName() { + return ""; + } + } + + void collectNames(List persons){ + List names = new ArrayList<>(); + for (Person person : persons) { + //some comment + names.add(person.getName()); + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectArrayListNonTrivialInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectArrayListNonTrivialInitializer.java new file mode 100644 index 000000000000..6a276c0033ce --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectArrayListNonTrivialInitializer.java @@ -0,0 +1,21 @@ +// "Replace with collect" "true" +import java.util.*; + +public class Collect { + class Person { + String getName() { + return ""; + } + } + + ArrayList foo() { + return new ArrayList<>(); + } + + void collectNames(List persons){ + List names = foo(); + for (Person person : persons) { + names.add(person.getName()); + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeNormalComments.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeNormalComments.java new file mode 100644 index 000000000000..f394dd7d9696 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeNormalComments.java @@ -0,0 +1,13 @@ +// "Replace with forEach" "true" +import java.util.ArrayList; +import java.util.List; + +class Sample { + List foo = new ArrayList<>(); + { + for (String s : foo) { + //some comment + System.out.println(s); + } + } +} diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/cast/insideString.java b/java/java-tests/testData/codeInsight/template/postfix/templates/cast/insideString.java new file mode 100644 index 000000000000..8e2d4d8e59cb --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/cast/insideString.java @@ -0,0 +1,5 @@ +public class Foo { + void m(Object o) { + Integer string = (Integer.parseInt("test.test.cast")); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/cast/insideString_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/cast/insideString_after.java new file mode 100644 index 000000000000..f51c17698074 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/cast/insideString_after.java @@ -0,0 +1,5 @@ +public class Foo { + void m(Object o) { + Integer string = (Integer.parseInt("test.test.cast ")); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/cast/singleArgument.java b/java/java-tests/testData/codeInsight/template/postfix/templates/cast/singleArgument.java new file mode 100644 index 000000000000..29bd8830d679 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/cast/singleArgument.java @@ -0,0 +1,5 @@ +public class Foo { + void m(Object o) { + Integer string = (Integer.parseInt("test.test".cast)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/cast/singleArgument_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/cast/singleArgument_after.java new file mode 100644 index 000000000000..61720cad312a --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/cast/singleArgument_after.java @@ -0,0 +1,5 @@ +public class Foo { + void m(Object o) { + Integer string = (Integer.parseInt((() "test.test"))); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java index 8af0821571d6..9c7ec52e1323 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java @@ -168,6 +168,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase doTest(); } + public void testIncorporationWithEqualsBoundsSubstitution() throws Exception { + doTest(); + } + private void doTest() throws Exception { doTest(false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java index 0cb2f0ecc65c..ccf0d5d59d05 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java @@ -39,6 +39,10 @@ public class MostSpecificResolutionTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testNestedLambdaSpecifics() throws Exception { + doTest(); + } + private void doTest() { doTest(true); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java index 826193c90f90..a3c2b12515eb 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java @@ -146,6 +146,14 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testIDEA121315() throws Exception { + doTest(); + } + + public void testIDEA118965comment() throws Exception { + doTest(); + } + private void doTest() { doTest(false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java index 83f0dfac5c1e..84cecc1b2d7d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java @@ -181,6 +181,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testSuperClassPotentiallyApplicableMembers() throws Exception { + doTest(); + } + private void doTest() { doTest(false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddExceptionToThrowsTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddExceptionToThrowsTest.java index f25775fb962b..5fa9b96f574d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddExceptionToThrowsTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddExceptionToThrowsTest.java @@ -1,5 +1,8 @@ package com.intellij.codeInsight.daemon.quickFix; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.testFramework.IdeaTestUtil; + public class AddExceptionToThrowsTest extends LightQuickFixParameterizedTestCase { public void test() throws Exception { doAllTests(); @@ -9,4 +12,9 @@ public class AddExceptionToThrowsTest extends LightQuickFixParameterizedTestCase protected String getBasePath() { return "/codeInsight/daemonCodeAnalyzer/quickFix/addToThrows"; } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy index 939e7520ecf5..cef42e9ea06d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -29,6 +29,7 @@ import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.impl.DocumentImpl import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiDocumentManager import com.intellij.psi.codeStyle.CodeStyleSettingsManager import com.intellij.psi.codeStyle.CommonCodeStyleSettings @@ -74,15 +75,50 @@ public class LiveTemplateTest extends LightCodeInsightFixtureTestCase { String group = "user"; final Template template = manager.createTemplate(templateName, group, templateText); template.addVariable("ARG", "", "", false); - final TemplateContextType contextType = - ContainerUtil.findInstance(TemplateContextType.EP_NAME.getExtensions(), JavaCodeContextType.class); + TemplateContextType contextType = contextType(JavaCodeContextType.class); ((TemplateImpl)template).getTemplateContext().setEnabled(contextType, true); addTemplate(template, testRootDisposable) - final Editor editor = getEditor(); manager.startTemplate(editor, (char)'\t'); checkResultByText(expected); } + + public void testTemplateWithSegmentsAtTheSamePosition_1() { + doTestTemplateWithThreeVariables("", "", "", "class A { void test() { for(TestValue1TestValue2TestValue3) {} } }") + } + + public void testTemplateWithSegmentsAtTheSamePosition_2() { + doTestTemplateWithThreeVariables("Def1", "Def2", "DefaultValue", "class A { void test() { for(Def1Def2DefaultValue) {} } }") + } + + public void testTemplateWithSegmentsAtTheSamePosition_3() { + doTestTemplateWithThreeVariables("", "DefaultValue", "", "class A { void test() { for(TestValue1DefaultValueTestValue3) {} } }") + } + + private void doTestTemplateWithThreeVariables(String firstDefaultValue, String secondDefaultValue, String thirdDefaultValue, + String expectedText) { + configureFromFileText("dummy.java", "class A { void test() { } }") + + TemplateManager manager = TemplateManager.getInstance(getProject()) + def templateName = "tst_template" + def templateGroup = "user" + final Template template = manager.createTemplate(templateName, templateGroup, 'for($TEST1$$TEST2$$TEST3$) {}') + template.addVariable("TEST1", "", StringUtil.wrapWithDoubleQuote(firstDefaultValue), true) + template.addVariable("TEST2", "", StringUtil.wrapWithDoubleQuote(secondDefaultValue), true) + template.addVariable("TEST3", "", StringUtil.wrapWithDoubleQuote(thirdDefaultValue), true) + ((TemplateImpl)template).templateContext.setEnabled(contextType(JavaCodeContextType.class), true) + addTemplate(template, testRootDisposable) + + startTemplate(templateName, templateGroup) + if (firstDefaultValue.empty) myFixture.type("TestValue1") + myFixture.type("\t") + if (secondDefaultValue.empty) myFixture.type("TestValue2") + myFixture.type("\t") + if (thirdDefaultValue.empty) myFixture.type("TestValue3") + myFixture.type("\t") + assert state == null + checkResultByText(expectedText); + } public void testTemplateWithArg1() throws IOException { doTestTemplateWithArg("tst", 'wrap($ARG$)', "tst arg", "wrap(arg)"); @@ -245,6 +281,10 @@ class Foo { TemplateManager.getInstance(getProject()).startTemplate(getEditor(), TemplateSettings.getInstance().getTemplate(name, group)); } + private static T contextType(Class clazz) { + ContainerUtil.findInstance(TemplateContextType.EP_NAME.getExtensions(), clazz) + } + private void configure() { myFixture.configureByFile(getTestName(false) + ".java"); } @@ -397,7 +437,7 @@ class Foo { assertTrue(isApplicable("class Foo { xxx void foo(String bar, xxx goo ) {} }", template)); assertTrue(isApplicable("class Foo { void foo(String[] bar) {} }", template)); assertTrue(isApplicable("class Foo { xxx String[] foo(String[] bar) {} }", template)); - + assertTrue(isApplicable("xxx package foo; class Foo {}", template)); } @@ -448,7 +488,7 @@ class Foo { new ListTemplatesHandler().invoke(project, editor, myFixture.file); assert myFixture.lookupElementStrings.containsAll(['iter', 'itco', 'toar']) - + myFixture.type('it') assert myFixture.lookupElementStrings[0].startsWith('it') assert LookupManager.getInstance(project).activeLookup.currentItem == myFixture.getLookupElements()[0] @@ -549,8 +589,7 @@ class Foo { public void testTemplateExpandingWithSelection() { final TemplateManager manager = TemplateManager.getInstance(getProject()); final Template template = manager.createTemplate("tpl", "user", 'expanded'); - final JavaStringContextType contextType = - ContainerUtil.findInstance(TemplateContextType.EP_NAME.getExtensions(), JavaStringContextType.class); + final JavaStringContextType contextType = contextType(JavaStringContextType.class); ((TemplateImpl)template).getTemplateContext().setEnabled(contextType, true); myFixture.configureByText("a.java", "class A { void f() { String s = \"tpl\"; } }") @@ -559,7 +598,7 @@ class Foo { myFixture.type '\t' myFixture.checkResult 'class A { void f() { Stri "; } }' } - + static void addTemplate(Template template, Disposable parentDisposable) { def settings = TemplateSettings.getInstance() settings.addTemplate(template); @@ -589,7 +628,7 @@ class Foo {{ class Foo {{ java.util.List list; String[][] s = list.toArray(new String[list.size()][]) -}}''' +}}''' } public void "test inner class name"() { @@ -722,4 +761,32 @@ class Foo { myFixture.checkResult('-foo-bar_goo- _foo_bar_goo_ c') } + public void "test use single member static import first"() { + myFixture.addClass("""package foo; +public class Bar { + public static void someMethod() {} + public static void someMethod(int a) {} +}""") + myFixture.configureByText "a.java", """ + +class Foo { + { + + } +} +""" + final TemplateManager manager = TemplateManager.getInstance(getProject()); + final Template template = manager.createTemplate("xxx", "user", 'foo.Bar.someMethod($END$)'); + template.setValue(USE_STATIC_IMPORT_IF_POSSIBLE, true); + + manager.startTemplate(editor, template); + myFixture.checkResult """import static foo.Bar.someMethod; + +class Foo { + { + someMethod() + } +} +""" + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/CastPostfixTemplateTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/CastPostfixTemplateTest.java index dbd4c1ddd20d..b7d4ef358823 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/CastPostfixTemplateTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/template/postfix/templates/CastPostfixTemplateTest.java @@ -1,10 +1,22 @@ +/* + * 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.codeInsight.template.postfix.templates; import org.jetbrains.annotations.NotNull; -/** - * @author ignatov - */ public class CastPostfixTemplateTest extends PostfixTemplateTestCase { @NotNull @Override @@ -12,4 +24,6 @@ public class CastPostfixTemplateTest extends PostfixTemplateTestCase { public void testSingleExpression() { doTest(); } // jdk mock needed public void testVoidExpression() { doTest(); } + public void testSingleArgument() { doTest(); } + public void testInsideString() { doTest(); } } diff --git a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java index bfd1de796f90..b0bc587fd406 100644 --- a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java +++ b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java @@ -600,4 +600,19 @@ public class FindManagerTest extends DaemonAnalyzerTestCase { findModel.setInCommentsOnly(true); FindManagerTestUtils.runFindForwardAndBackward(myFindManager, findModel, text, "java"); } + + public void testPlusWholeWordsOnly() throws Exception { + createFile(myModule, "A.java", "3 + '+' + 2"); + + FindModel findModel = FindManagerTestUtils.configureFindModel("'+' +"); + findModel.setMultipleFiles(true); + + assertSize(1, findUsages(findModel)); + + findModel.setCaseSensitive(true); + assertSize(1, findUsages(findModel)); + + findModel.setWholeWordsOnly(true); + assertSize(1, findUsages(findModel)); + } } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java index a5b34fc53a17..c4cd499ccd96 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java @@ -545,11 +545,15 @@ public class JavaFormatterSpaceTest extends AbstractJavaFormatterTest { doTextTest( "class C<@TA(1)T> {\n" + " L<@TA(2)A> f = (@TA(3) A) new @TA(4) A() {\n" + + " void m(@TA(6) int @TA(7)[] p) {\n" + + " }\n" + " };\n" + "}", "class C<@TA(1) T> {\n" + " L<@TA(2) A> f = (@TA(3) A) new @TA(4) A() {\n" + + " void m(@TA(6) int @TA(7) [] p) {\n" + + " }\n" + " };\n" + "}" ); diff --git a/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveClassTest.java b/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveClassTest.java index 4c925fe931b2..df27bc351bb8 100644 --- a/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveClassTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveClassTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * 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. @@ -162,7 +162,7 @@ public class ResolveClassTest extends ResolveTestCase { configureDependency(); PsiReference ref = configure(); PsiElement target = ((PsiJavaReference)ref).advancedResolve(true).getElement(); - assertTrue(target instanceof PsiClass); + assertTrue(String.valueOf(target), target instanceof PsiClass); } public void testTwoModules2() throws Exception { @@ -203,7 +203,8 @@ public class ResolveClassTest extends ResolveTestCase { PsiReference ref = configure(); long start = System.currentTimeMillis(); assertNull(ref.resolve()); - PlatformTestUtil.assertTiming("exponent?", 200, System.currentTimeMillis() - start); + long elapsed = System.currentTimeMillis() - start; + PlatformTestUtil.assertTiming("exponent?", 200, elapsed); } public void testStaticImportNetwork() throws Exception { diff --git a/java/jdkAnnotations/java/util/regex/annotations.xml b/java/jdkAnnotations/java/util/regex/annotations.xml index 2afbcb551b37..c12985e6c529 100644 --- a/java/jdkAnnotations/java/util/regex/annotations.xml +++ b/java/jdkAnnotations/java/util/regex/annotations.xml @@ -14,6 +14,9 @@ + + + diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java index 689db8ac814f..334ba15f4a88 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java @@ -30,17 +30,14 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.File; import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; /** @noinspection SynchronizationOnLocalVariableOrMethodParameter*/ final class FilesDelta { private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.fs.FilesDelta"); private final Set myDeletedPaths = Collections.synchronizedSet(new THashSet(FileUtil.PATH_HASHING_STRATEGY)); - private final Map> myFilesToRecompile = Collections.synchronizedMap(new HashMap>()); + private final Map> myFilesToRecompile = Collections.synchronizedMap(new LinkedHashMap>()); public void save(DataOutput out) throws IOException { out.writeInt(myDeletedPaths.size()); diff --git a/platform/analysis-impl/src/com/intellij/application/options/colors/ScopeAttributesUtil.java b/platform/analysis-impl/src/com/intellij/application/options/colors/ScopeAttributesUtil.java index 4466f6873325..530d7a096603 100644 --- a/platform/analysis-impl/src/com/intellij/application/options/colors/ScopeAttributesUtil.java +++ b/platform/analysis-impl/src/com/intellij/application/options/colors/ScopeAttributesUtil.java @@ -16,11 +16,21 @@ package com.intellij.application.options.colors; import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.util.containers.ConcurrentFactoryMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class ScopeAttributesUtil { + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + private static final ConcurrentFactoryMap ourCache = new ConcurrentFactoryMap() { + @Nullable + @Override + protected TextAttributesKey create(String scope) { + return TextAttributesKey.find("SCOPE_KEY_" + scope); + } + }; @NotNull public static TextAttributesKey getScopeTextAttributeKey(@NotNull String scope) { - return TextAttributesKey.find("SCOPE_KEY_" + scope); + return ourCache.get(scope); } } diff --git a/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceSet.java b/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceSet.java index c0a312085b7f..47eae3c51030 100644 --- a/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceSet.java +++ b/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceSet.java @@ -83,7 +83,7 @@ public class FileReferenceSet { @Nullable private FileType[] mySuitableFileTypes; public FileReferenceSet(String str, - PsiElement element, + @NotNull PsiElement element, int startInElement, PsiReferenceProvider provider, boolean caseSensitive, @@ -93,7 +93,7 @@ public class FileReferenceSet { } public FileReferenceSet(String str, - PsiElement element, + @NotNull PsiElement element, int startInElement, PsiReferenceProvider provider, boolean caseSensitive, @@ -131,19 +131,16 @@ public class FileReferenceSet { return Collections.emptyList(); } - public static FileReferenceSet createSet(PsiElement element, + public static FileReferenceSet createSet(@NotNull PsiElement element, final boolean soft, boolean endingSlashNotAllowed, final boolean urlEncoded) { - String text; - int offset; - final ElementManipulator manipulator = ElementManipulators.getManipulator(element); assert manipulator != null; final TextRange range = manipulator.getRangeInElement(element); - offset = range.getStartOffset(); - text = range.substring(element.getText()); + int offset = range.getStartOffset(); + String text = range.substring(element.getText()); for (final FileReferenceHelper helper : FileReferenceHelperRegistrar.getHelpers()) { text = helper.trimUrl(text); } @@ -163,7 +160,7 @@ public class FileReferenceSet { public FileReferenceSet(String str, - PsiElement element, + @NotNull PsiElement element, int startInElement, @Nullable PsiReferenceProvider provider, final boolean isCaseSensitive) { @@ -172,7 +169,7 @@ public class FileReferenceSet { public FileReferenceSet(@NotNull String str, - PsiElement element, + @NotNull PsiElement element, int startInElement, PsiReferenceProvider provider, final boolean isCaseSensitive, @@ -181,7 +178,6 @@ public class FileReferenceSet { } public FileReferenceSet(@NotNull final PsiElement element) { - myElement = element; TextRange range = ElementManipulators.getValueTextRange(element); myStartInElement = range.getStartOffset(); diff --git a/platform/core-api/src/com/intellij/openapi/util/AsyncValueLoader.java b/platform/core-api/src/com/intellij/openapi/util/AsyncValueLoader.java index c699df1a088b..97724a9d9605 100644 --- a/platform/core-api/src/com/intellij/openapi/util/AsyncValueLoader.java +++ b/platform/core-api/src/com/intellij/openapi/util/AsyncValueLoader.java @@ -107,9 +107,9 @@ public abstract class AsyncValueLoader { } } - protected abstract void load(AsyncResult asyncResult) throws IOException; + protected abstract void load(@NotNull AsyncResult result) throws IOException; - protected boolean checkFreshness(T asyncResult) { + protected boolean checkFreshness(T result) { return true; } } \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/openapi/util/AsyncValueLoaderManager.java b/platform/core-api/src/com/intellij/openapi/util/AsyncValueLoaderManager.java index 6a658a746f58..65a08deee027 100644 --- a/platform/core-api/src/com/intellij/openapi/util/AsyncValueLoaderManager.java +++ b/platform/core-api/src/com/intellij/openapi/util/AsyncValueLoaderManager.java @@ -1,6 +1,6 @@ package com.intellij.openapi.util; -import com.intellij.openapi.util.AsyncResult; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; @@ -13,11 +13,11 @@ public abstract class AsyncValueLoaderManager { this.fieldUpdater = fieldUpdater; } - public boolean checkFreshness(HOST host, VALUE value) { + public boolean checkFreshness(@NotNull HOST host, @NotNull VALUE value) { return true; } - public abstract void load(HOST host, AsyncResult result); + public abstract void load(@NotNull HOST host, @NotNull AsyncResult result); public final void reset(HOST host) { fieldUpdater.set(host, null); diff --git a/platform/core-api/src/com/intellij/psi/ElementManipulators.java b/platform/core-api/src/com/intellij/psi/ElementManipulators.java index 886e22e26d33..bd80267622b1 100644 --- a/platform/core-api/src/com/intellij/psi/ElementManipulators.java +++ b/platform/core-api/src/com/intellij/psi/ElementManipulators.java @@ -44,7 +44,7 @@ public class ElementManipulators extends ClassExtension { return INSTANCE.forClass(element.getClass()); } - public static int getOffsetInElement(final PsiElement element) { + public static int getOffsetInElement(@NotNull PsiElement element) { final ElementManipulator manipulator = getNotNullManipulator(element); return manipulator.getRangeInElement(element).getStartOffset(); } diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java index 6be740bc6364..8a343f7279dd 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java @@ -38,8 +38,11 @@ import com.intellij.util.IncorrectOperationException; import com.intellij.util.LocalTimeCounter; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.IntArrayList; import com.intellij.util.text.CharArrayUtil; import com.intellij.util.text.ImmutableText; +import gnu.trove.TIntObjectHashMap; +import gnu.trove.TObjectProcedure; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,7 +50,9 @@ import org.jetbrains.annotations.TestOnly; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class DocumentImpl extends UserDataHolderBase implements DocumentEx { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.DocumentImpl"); @@ -166,6 +171,9 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { /** * @return true if stripping was completed successfully, false if the document prevented stripping by e.g. caret being in the way + * + * @deprecated should be replaced with {@link #stripTrailingSpaces(com.intellij.openapi.project.Project, boolean, boolean, java.util.List)} + * once multicaret logic will become unconditional (not controlled by configuration flag) */ public boolean stripTrailingSpaces(@Nullable final Project project, boolean inChangedLinesOnly, @@ -233,7 +241,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { boolean markAsNeedsStrippingLater = false; CharSequence text = myText; - Map> caretMarkers = new HashMap>(caretOffsets.size()); + TIntObjectHashMap> caretMarkers = new TIntObjectHashMap>(caretOffsets.size()); try { if (!virtualSpaceEnabled) { for (Integer caretOffset : caretOffsets) { @@ -291,18 +299,22 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { } } finally { - for (List markerList : caretMarkers.values()) { - if (markerList != null) { - for (RangeMarker marker : markerList) { - try { - marker.dispose(); - } - catch (Exception e) { - LOG.error(e); + caretMarkers.forEachValue(new TObjectProcedure>() { + @Override + public boolean execute(List markerList) { + if (markerList != null) { + for (RangeMarker marker : markerList) { + try { + marker.dispose(); + } + catch (Exception e) { + LOG.error(e); + } } } + return true; } - } + }); } return markAsNeedsStrippingLater; } @@ -670,17 +682,17 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { getLineSet().clearModificationFlags(); } - public void clearLineModificationFlagsExcept(@NotNull List caretLines) { - List modifiedLines = new ArrayList(caretLines.size()); + public void clearLineModificationFlagsExcept(@NotNull int[] caretLines) { + IntArrayList modifiedLines = new IntArrayList(caretLines.length); LineSet lineSet = getLineSet(); - for (Integer line : caretLines) { - if (line != null && line >= 0 && line < lineSet.getLineCount() && lineSet.isModified(line)) { + for (int line : caretLines) { + if (line >= 0 && line < lineSet.getLineCount() && lineSet.isModified(line)) { modifiedLines.add(line); } } clearLineModificationFlags(); - for (Integer line : modifiedLines) { - lineSet.setModified(line); + for (int i = 0; i < modifiedLines.size(); i++) { + lineSet.setModified(modifiedLines.get(i)); } } diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java index 881f68d837f8..658d893cc33e 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java @@ -114,6 +114,7 @@ public class UISettings implements PersistentStateComponent, Exporta public boolean PRESENTATION_MODE = false; public int PRESENTATION_MODE_FONT_SIZE = 24; public boolean MARK_MODIFIED_TABS_WITH_ASTERISK = false; + public boolean SHOW_TABS_TOOLTIPS = true; public boolean SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES = false; private final EventListenerList myListenerList; diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form index 4825e02f9db7..9872c7c624aa 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form @@ -3,12 +3,12 @@ - + - + @@ -69,7 +69,9 @@ - + + + @@ -91,6 +93,14 @@ + + + + + + + + diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java index cbb57dbb1081..4e1de817255a 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java @@ -41,6 +41,7 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { private JRadioButton myActivateLeftEditorOnCloseRadio; private JRadioButton myActivateMRUEditorOnCloseRadio; private JCheckBox myCbModifiedTabsMarkedWithAsterisk; + private JCheckBox myShowTabsTooltipsCheckBox; private JCheckBox myShowCloseButtonOnCheckBox; private JCheckBox myShowDirectoryInTabCheckBox; private JRadioButton myActivateRightNeighbouringTabRadioButton; @@ -71,12 +72,14 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { myHideKnownExtensions.setEnabled(false); myScrollTabLayoutInEditorCheckBox.setEnabled(false); myCbModifiedTabsMarkedWithAsterisk.setEnabled(false); + myShowTabsTooltipsCheckBox.setEnabled(false); myShowCloseButtonOnCheckBox.setEnabled(false); myShowDirectoryInTabCheckBox.setEnabled(false); } else { myHideKnownExtensions.setEnabled(true); myScrollTabLayoutInEditorCheckBox.setEnabled(true); myCbModifiedTabsMarkedWithAsterisk.setEnabled(true); + myShowTabsTooltipsCheckBox.setEnabled(true); myShowCloseButtonOnCheckBox.setEnabled(true); myShowDirectoryInTabCheckBox.setEnabled(true); } @@ -110,6 +113,7 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { UISettings uiSettings=UISettings.getInstance(); myCbModifiedTabsMarkedWithAsterisk.setSelected(uiSettings.MARK_MODIFIED_TABS_WITH_ASTERISK); + myShowTabsTooltipsCheckBox.setSelected(uiSettings.SHOW_TABS_TOOLTIPS); myScrollTabLayoutInEditorCheckBox.setSelected(uiSettings.SCROLL_TAB_LAYOUT_IN_EDITOR); myEditorTabPlacement.setSelectedItem(uiSettings.EDITOR_TAB_PLACEMENT); myHideKnownExtensions.setSelected(uiSettings.HIDE_KNOWN_EXTENSION_IN_TABS); @@ -141,6 +145,9 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { boolean uiSettingsChanged = uiSettings.MARK_MODIFIED_TABS_WITH_ASTERISK != myCbModifiedTabsMarkedWithAsterisk.isSelected(); uiSettings.MARK_MODIFIED_TABS_WITH_ASTERISK = myCbModifiedTabsMarkedWithAsterisk.isSelected(); + if (isModified(myShowTabsTooltipsCheckBox, uiSettings.SHOW_TABS_TOOLTIPS)) uiSettingsChanged = true; + uiSettings.SHOW_TABS_TOOLTIPS = myShowTabsTooltipsCheckBox.isSelected(); + if (isModified(myScrollTabLayoutInEditorCheckBox, uiSettings.SCROLL_TAB_LAYOUT_IN_EDITOR)) uiSettingsChanged = true; uiSettings.SCROLL_TAB_LAYOUT_IN_EDITOR = myScrollTabLayoutInEditorCheckBox.isSelected(); @@ -182,6 +189,7 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { public boolean isModified() { final UISettings uiSettings = UISettings.getInstance(); boolean isModified = isModified(myCbModifiedTabsMarkedWithAsterisk, uiSettings.MARK_MODIFIED_TABS_WITH_ASTERISK); + isModified |= isModified(myShowTabsTooltipsCheckBox, uiSettings.SHOW_TABS_TOOLTIPS); isModified |= isModified(myEditorTabLimitField, uiSettings.EDITOR_TAB_LIMIT); int tabPlacement = ((Integer)myEditorTabPlacement.getSelectedItem()).intValue(); isModified |= tabPlacement != uiSettings.EDITOR_TAB_PLACEMENT; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateSettingsEditor.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateSettingsEditor.java index fe0506e9ff34..efc279c49c85 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateSettingsEditor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateSettingsEditor.java @@ -65,6 +65,7 @@ import java.util.List; public class LiveTemplateSettingsEditor extends JPanel { private final TemplateImpl myTemplate; + private final Runnable myNodeChanged; private final JTextField myKeyField; private final JTextField myDescription; @@ -80,19 +81,20 @@ public class LiveTemplateSettingsEditor extends JPanel { private static final String TAB = CodeInsightBundle.message("template.shortcut.tab"); private static final String ENTER = CodeInsightBundle.message("template.shortcut.enter"); private final Map myOptions; - private final Map myContext; + private final TemplateContext myContext; private JBPopup myContextPopup; private Dimension myLastSize; public LiveTemplateSettingsEditor(TemplateImpl template, final String defaultShortcut, Map options, - Map context, final Runnable nodeChanged, boolean allowNoContext) { + TemplateContext context, final Runnable nodeChanged, boolean allowNoContext) { super(new BorderLayout()); myOptions = options; myContext = context; myTemplate = template; + myNodeChanged = nodeChanged; myDefaultShortcutItem = CodeInsightBundle.message("dialog.edit.template.shortcut.default", defaultShortcut); myKeyField=new JTextField(); @@ -106,14 +108,14 @@ public class LiveTemplateSettingsEditor extends JPanel { @Override protected void textChanged(javax.swing.event.DocumentEvent e) { myTemplate.setKey(myKeyField.getText().trim()); - nodeChanged.run(); + myNodeChanged.run(); } }); myDescription.getDocument().addDocumentListener(new com.intellij.ui.DocumentAdapter() { @Override protected void textChanged(javax.swing.event.DocumentEvent e) { myTemplate.setDescription(myDescription.getText().trim()); - nodeChanged.run(); + myNodeChanged.run(); } }); @@ -172,6 +174,7 @@ public class LiveTemplateSettingsEditor extends JPanel { myTemplate.setString(myTemplateEditor.getDocument().getText()); applyVariables(updateVariablesByTemplateText()); + myNodeChanged.run(); } } ); @@ -290,8 +293,8 @@ public class LiveTemplateSettingsEditor extends JPanel { private List getApplicableContexts() { ArrayList result = new ArrayList(); - for (TemplateContextType type : myContext.keySet()) { - if (myContext.get(type).booleanValue()) { + for (TemplateContextType type : TemplateManagerImpl.getAllContextTypes()) { + if (myContext.isExplicitlyEnabled(type)) { result.add(type); } } @@ -379,11 +382,11 @@ public class LiveTemplateSettingsEditor extends JPanel { return false; } - static JPanel createPopupContextPanel(final Runnable onChange, final Map context) { + static JPanel createPopupContextPanel(final Runnable onChange, final TemplateContext context) { JPanel panel = new JPanel(new BorderLayout()); MultiMap hierarchy = MultiMap.createLinked(); - for (TemplateContextType type : context.keySet()) { + for (TemplateContextType type : TemplateManagerImpl.getAllContextTypes()) { hierarchy.putValue(type.getBaseContextType(), type); } @@ -401,7 +404,7 @@ public class LiveTemplateSettingsEditor extends JPanel { protected void onNodeStateChanged(CheckedTreeNode node) { final TemplateContextType type = (TemplateContextType)((Pair)node.getUserObject()).first; if (type != null) { - context.put(type, node.isChecked()); + context.putValue(type, node.isChecked()); } onChange.run(); @@ -434,21 +437,21 @@ public class LiveTemplateSettingsEditor extends JPanel { private static void addContextNode(MultiMap hierarchy, CheckedTreeNode parent, - TemplateContextType type, Map context) { + TemplateContextType type, TemplateContext context) { final Collection children = hierarchy.get(type); final String name = UIUtil.removeMnemonic(type.getPresentableName()); final CheckedTreeNode node = new CheckedTreeNode(Pair.create(children.isEmpty() ? type : null, name)); parent.add(node); if (children.isEmpty()) { - node.setChecked(context.get(type)); + node.setChecked(context.isExplicitlyEnabled(type)); } else { for (TemplateContextType child : children) { addContextNode(hierarchy, node, child, context); } final CheckedTreeNode other = new CheckedTreeNode(Pair.create(type, "Other")); - other.setChecked(context.get(type)); + other.setChecked(context.isExplicitlyEnabled(type)); node.add(other); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateContext.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateContext.java index 7a814d2e3717..e632a6848d03 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateContext.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateContext.java @@ -45,10 +45,11 @@ public class TemplateContext { //noinspection NestedSynchronizedStatement synchronized (defaultContext == null ? myContextStates : defaultContext.myContextStates) { for (TemplateContextType contextType : TemplateManagerImpl.getAllContextTypes()) { - String context = contextType.getContextId(); - Boolean myStateInContext = myContextStates.get(context); - if (myStateInContext != null && differsFromDefault(defaultContext, context, myStateInContext)) { - result.put(contextType, myStateInContext); + Boolean ownValue = getOwnValue(contextType); + if (ownValue != null) { + if (defaultContext == null || isEnabled(contextType) != defaultContext.isEnabled(contextType)) { + result.put(contextType, ownValue); + } } } } @@ -56,16 +57,8 @@ public class TemplateContext { return result; } - private static boolean differsFromDefault(@Nullable TemplateContext defaultContext, String context, boolean myStateInContext) { - Boolean defaultStateInContext = defaultContext == null ? null : defaultContext.myContextStates.get(context); - if (defaultStateInContext == null) { - return true; - } - return myStateInContext != defaultStateInContext; - } - public boolean isEnabled(TemplateContextType contextType) { - Boolean storedValue = isEnabledBare(contextType); + Boolean storedValue = getOwnValue(contextType); if (storedValue == null) { TemplateContextType baseContextType = contextType.getBaseContextType(); if (baseContextType != null && !(baseContextType instanceof EverywhereContextType)) { @@ -76,7 +69,18 @@ public class TemplateContext { return storedValue.booleanValue(); } - private Boolean isEnabledBare(TemplateContextType contextType) { + public void putValue(TemplateContextType context, boolean enabled) { + synchronized (myContextStates) { + myContextStates.put(context.getContextId(), enabled); + } + } + + public boolean isExplicitlyEnabled(TemplateContextType contextType) { + return Boolean.TRUE.equals(getOwnValue(contextType)); + } + + @Nullable + public Boolean getOwnValue(TemplateContextType contextType) { synchronized (myContextStates) { return myContextStates.get(contextType.getContextId()); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateEditorUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateEditorUtil.java index 2d7c74056e79..1570bb18c040 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateEditorUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateEditorUtil.java @@ -45,8 +45,6 @@ import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Map; - public class TemplateEditorUtil { private TemplateEditorUtil() {} @@ -54,16 +52,16 @@ public class TemplateEditorUtil { return createEditor(isReadOnly, text, null); } - public static Editor createEditor(boolean isReadOnly, CharSequence text, @Nullable Map context) { + public static Editor createEditor(boolean isReadOnly, CharSequence text, @Nullable TemplateContext context) { final Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext()); return createEditor(isReadOnly, createDocument(text, context, project), project); } - private static Document createDocument(CharSequence text, @Nullable Map context, Project project) { + private static Document createDocument(CharSequence text, @Nullable TemplateContext context, Project project) { if (context != null) { - for (Map.Entry entry : context.entrySet()) { - if (entry.getValue()) { - return entry.getKey().createDocument(text, project); + for (TemplateContextType type : TemplateManagerImpl.getAllContextTypes()) { + if (context.isExplicitlyEnabled(type)) { + return type.createDocument(text, project); } } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateImpl.java index ee18f37a7990..48c98cdc170d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateImpl.java @@ -18,7 +18,6 @@ package com.intellij.codeInsight.template.impl; import com.intellij.codeInsight.template.Expression; import com.intellij.codeInsight.template.Template; -import com.intellij.codeInsight.template.TemplateContextType; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.options.SchemeElement; import com.intellij.openapi.util.text.StringUtil; @@ -176,24 +175,35 @@ public class TemplateImpl extends Template implements SchemeElement { @Override public TemplateImpl copy() { TemplateImpl template = new TemplateImpl(myKey, myString, myGroupName); - template.myId = myId; - template.myDescription = myDescription; - template.myShortcutChar = myShortcutChar; - template.isToReformat = isToReformat; - template.isToShortenLongNames = isToShortenLongNames; - template.myIsInline = myIsInline; - template.myTemplateContext = myTemplateContext.createCopy(); - template.isDeactivated = isDeactivated; + template.resetFrom(this); + return template; + } + + public void resetFrom(TemplateImpl another) { + removeAllParsed(); + toParseSegments = another.toParseSegments; + + myKey = another.getKey(); + myString = another.myString; + myTemplateText = another.myTemplateText; + myGroupName = another.myGroupName; + myId = another.myId; + myDescription = another.myDescription; + myShortcutChar = another.myShortcutChar; + isToReformat = another.isToReformat; + isToShortenLongNames = another.isToShortenLongNames; + myIsInline = another.myIsInline; + myTemplateContext = another.myTemplateContext.createCopy(); + isDeactivated = another.isDeactivated; for (Property property : Property.values()) { - boolean value = getValue(property); + boolean value = another.getValue(property); if (value != Template.getDefaultValue(property)) { - template.setValue(property, value); + setValue(property, value); } } - for (Variable variable : myVariables) { - template.addVariable(variable.getName(), variable.getExpressionString(), variable.getDefaultValueString(), variable.isAlwaysStopAt()); + for (Variable variable : another.myVariables) { + addVariable(variable.getName(), variable.getExpressionString(), variable.getDefaultValueString(), variable.isAlwaysStopAt()); } - return template; } public boolean isToReformat() { @@ -436,14 +446,8 @@ public class TemplateImpl extends Template implements SchemeElement { return context; } - public Map createContext(){ - - Map context = new LinkedHashMap(); - for (TemplateContextType processor : TemplateManagerImpl.getAllContextTypes()) { - context.put(processor, getTemplateContext().isEnabled(processor)); - } - return context; - + public TemplateContext createContext() { + return getTemplateContext().createCopy(); } public boolean contextsEqual(TemplateImpl defaultTemplate) { @@ -456,10 +460,8 @@ public class TemplateImpl extends Template implements SchemeElement { } } - public void applyContext(final Map context) { - for (Map.Entry entry : context.entrySet()) { - getTemplateContext().setEnabled(entry.getKey(), entry.getValue().booleanValue()); - } + public void applyContext(final TemplateContext context) { + myTemplateContext = context.createCopy(); } public boolean skipOnStart(int i) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java index 60b6755f53b5..1aed0f448a6f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java @@ -19,7 +19,6 @@ package com.intellij.codeInsight.template.impl; import com.intellij.application.options.ExportSchemeAction; import com.intellij.application.options.SchemesToImportPopup; import com.intellij.codeInsight.CodeInsightBundle; -import com.intellij.codeInsight.template.TemplateContextType; import com.intellij.ide.DataManager; import com.intellij.ide.dnd.*; import com.intellij.ide.dnd.aware.DnDAwareTree; @@ -45,6 +44,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; import com.intellij.util.ui.tree.TreeUtil; import com.intellij.util.ui.update.UiNotifyConnector; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -94,7 +94,7 @@ public class TemplateListPanel extends JPanel implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.template.impl.TemplateListPanel"); private final Map> myTemplateOptions = new LinkedHashMap>(); - private final Map> myTemplateContext = new LinkedHashMap>(); + private final Map myTemplateContext = ContainerUtil.newLinkedHashMap(); private final JPanel myDetailsPanel = new JPanel(new CardLayout()); private LiveTemplateSettingsEditor myCurrentTemplateEditor; @@ -152,7 +152,7 @@ public class TemplateListPanel extends JPanel implements Disposable { UiNotifyConnector.doWhenFirstShown(this, new Runnable() { @Override public void run() { - updateTemplateDetails(false); + updateTemplateDetails(false, false); } }); @@ -208,17 +208,22 @@ public class TemplateListPanel extends JPanel implements Disposable { List originalGroups = templateSettings.getTemplateGroups(); List newGroups = getTemplateGroups(); - if (checkAreEqual(collectTemplates(originalGroups), collectTemplates(newGroups))) return false; + List originalGroup = collectTemplates(originalGroups); + List newGroup = collectTemplates(newGroups); + + if (checkAreEqual(originalGroup, newGroup)) return false; + if (isTest) { //noinspection UseOfSystemOutOrSystemErr - System.err.println("LiveTemplatesConfig: originalGroups="+originalGroups+"; collectTemplates(originalGroups)="+collectTemplates(originalGroups)+";\n newGroups="+newGroups+"; collectTemplates(newGroups)="+collectTemplates(newGroups)); + System.err.println("LiveTemplatesConfig: originalGroups="+originalGroups+"; collectTemplates(originalGroups)="+ + originalGroup +";\n newGroups="+newGroups+"; collectTemplates(newGroups)="+ newGroup); } return true; } public void editTemplate(TemplateImpl template) { selectTemplate(template.getGroupName(), template.getKey()); - updateTemplateDetails(true); + updateTemplateDetails(true, false); } @Nullable @@ -247,43 +252,13 @@ public class TemplateListPanel extends JPanel implements Disposable { return result; } - private boolean checkAreEqual(final List originalGroup, final List newGroup) { + private boolean checkAreEqual(List originalGroup, List newGroup) { if (originalGroup.size() != newGroup.size()) return false; for (int i = 0; i < newGroup.size(); i++) { - TemplateImpl newTemplate = newGroup.get(i); - newTemplate.parseSegments(); - TemplateImpl originalTemplate = originalGroup.get(i); - originalTemplate.parseSegments(); - if (!originalTemplate.equals(newTemplate)) { + if (templatesDiffer(newGroup.get(i), originalGroup.get(i))) { return false; } - - if (originalTemplate.isDeactivated() != newTemplate.isDeactivated()) { - return false; - } - - if (!newTemplate.getVariables().equals(originalTemplate.getVariables())) { - return false; - } - - if (!areOptionsEqual(newTemplate, originalTemplate)) { - return false; - } - - if (!areContextsEqual(newTemplate, originalTemplate)) { - return false; - } - } - - return true; - } - - private boolean areContextsEqual(final TemplateImpl newTemplate, final TemplateImpl originalTemplate) { - Map templateContext = getTemplateContext(newTemplate); - for (TemplateContextType processor : templateContext.keySet()) { - if (originalTemplate.getTemplateContext().isEnabled(processor) != templateContext.get(processor).booleanValue()) - return false; } return true; } @@ -296,7 +271,7 @@ public class TemplateListPanel extends JPanel implements Disposable { return true; } - private Map getTemplateContext(final TemplateImpl newTemplate) { + private TemplateContext getTemplateContext(final TemplateImpl newTemplate) { return myTemplateContext.get(getKey(newTemplate)); } @@ -324,7 +299,7 @@ public class TemplateListPanel extends JPanel implements Disposable { private void createTemplateEditor(final TemplateImpl template, String shortcut, Map options, - Map context) { + TemplateContext context) { myCurrentTemplateEditor = new LiveTemplateSettingsEditor(template, shortcut, options, context, new Runnable() { @Override public void run() { @@ -482,7 +457,7 @@ public class TemplateListPanel extends JPanel implements Disposable { myTemplateContext.put(getKey(template), template.createContext()); registerTemplate(template); - updateTemplateDetails(true); + updateTemplateDetails(true, false); } private static int getKey(final TemplateImpl template) { @@ -498,10 +473,10 @@ public class TemplateListPanel extends JPanel implements Disposable { TemplateImpl template = orTemplate.copy(); template.setKey(ABBREVIATION); myTemplateOptions.put(getKey(template), new HashMap(getTemplateOptions(orTemplate))); - myTemplateContext.put(getKey(template), new HashMap(getTemplateContext(orTemplate))); + myTemplateContext.put(getKey(template), getTemplateContext(orTemplate).createCopy()); registerTemplate(template); - updateTemplateDetails(true); + updateTemplateDetails(true, false); } private int getSingleSelectedIndex() { @@ -554,9 +529,12 @@ public class TemplateListPanel extends JPanel implements Disposable { value = ((DefaultMutableTreeNode)value).getUserObject(); if (value instanceof TemplateImpl) { - getTextRenderer().append (((TemplateImpl)value).getKey(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - String description = ((TemplateImpl)value).getDescription(); - if (description != null && description.length() > 0) { + TemplateImpl template = (TemplateImpl)value; + TemplateImpl defaultTemplate = TemplateSettings.getInstance().getDefaultTemplate(template); + Color fgColor = defaultTemplate != null && templatesDiffer(template, defaultTemplate) ? JBColor.BLUE : null; + getTextRenderer().append(template.getKey(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, fgColor)); + String description = template.getDescription(); + if (StringUtil.isNotEmpty(description)) { getTextRenderer().append (" (" + description + ")", SimpleTextAttributes.GRAY_ATTRIBUTES); } } @@ -612,7 +590,7 @@ public class TemplateListPanel extends JPanel implements Disposable { myAlarm.addRequest(new Runnable() { @Override public void run() { - updateTemplateDetails(false); + updateTemplateDetails(false, false); } }, 100); } @@ -679,6 +657,15 @@ public class TemplateListPanel extends JPanel implements Disposable { } + private boolean templatesDiffer(@NotNull TemplateImpl template, @NotNull TemplateImpl defaultTemplate) { + template.parseSegments(); + defaultTemplate.parseSegments(); + return !template.equals(defaultTemplate) || + !template.getVariables().equals(defaultTemplate.getVariables()) || + !areOptionsEqual(template, defaultTemplate) || + !getTemplateContext(template).getDifference(defaultTemplate.getTemplateContext()).isEmpty(); + } + private ToolbarDecorator initToolbar() { ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myTree) .setAddAction(new AnActionButtonRunnable() { @@ -855,10 +842,7 @@ public class TemplateListPanel extends JPanel implements Disposable { @Override public void actionPerformed(AnActionEvent e) { Map templates = getSelectedTemplates(); - Map context = ContainerUtil.newHashMap(); - for (TemplateContextType type : TemplateManagerImpl.getAllContextTypes()) { - context.put(type, Boolean.FALSE); - } + TemplateContext context = new TemplateContext(); JPanel contextPanel = LiveTemplateSettingsEditor.createPopupContextPanel(EmptyRunnable.INSTANCE, context); DialogBuilder builder = new DialogBuilder(TemplateListPanel.this); builder.setCenterPanel(contextPanel); @@ -866,9 +850,43 @@ public class TemplateListPanel extends JPanel implements Disposable { int result = builder.show(); if (result == DialogWrapper.OK_EXIT_CODE) { for (TemplateImpl template : templates.keySet()) { - getTemplateContext(template).putAll(context); + myTemplateContext.put(getKey(template), context); } - } + } + updateTemplateDetails(false, true); + myTree.repaint(); + } + }; + final DumbAwareAction revert = new DumbAwareAction("Restore defaults", "Restore default setting for the selected templates", null) { + + @Override + public void update(AnActionEvent e) { + boolean enabled = false; + Map templates = getSelectedTemplates(); + for (TemplateImpl template : templates.keySet()) { + TemplateImpl defaultTemplate = TemplateSettings.getInstance().getDefaultTemplate(template); + if (defaultTemplate != null && templatesDiffer(template, defaultTemplate)) { + enabled = true; + } + } + e.getPresentation().setEnabled(enabled); + e.getPresentation().setVisible(enabled); + super.update(e); + } + + @Override + public void actionPerformed(AnActionEvent e) { + Map templates = getSelectedTemplates(); + for (TemplateImpl template : templates.keySet()) { + TemplateImpl defaultTemplate = TemplateSettings.getInstance().getDefaultTemplate(template); + if (defaultTemplate != null) { + myTemplateOptions.put(getKey(template), defaultTemplate.createOptions()); + myTemplateContext.put(getKey(template), defaultTemplate.createContext()); + template.resetFrom(defaultTemplate); + } + } + updateTemplateDetails(false, true); + myTree.repaint(); } }; @@ -880,6 +898,7 @@ public class TemplateListPanel extends JPanel implements Disposable { group.add(rename); group.add(move); group.add(changeContext); + group.add(revert); ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group).getComponent().show(comp, x, y); } }); @@ -925,7 +944,7 @@ public class TemplateListPanel extends JPanel implements Disposable { } } - private void updateTemplateDetails(boolean focusKey) { + private void updateTemplateDetails(boolean focusKey, boolean forceReload) { int selected = getSingleSelectedIndex(); CardLayout layout = (CardLayout)myDetailsPanel.getLayout(); if (selected < 0 || getTemplate(selected) == null) { @@ -933,7 +952,7 @@ public class TemplateListPanel extends JPanel implements Disposable { } else { TemplateImpl newTemplate = getTemplate(selected); - if (myCurrentTemplateEditor == null || myCurrentTemplateEditor.getTemplate() != newTemplate) { + if (myCurrentTemplateEditor == null || forceReload || myCurrentTemplateEditor.getTemplate() != newTemplate) { if (myCurrentTemplateEditor != null) { myCurrentTemplateEditor.dispose(); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java index 031476f39e9a..183ca778ced7 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -73,12 +73,19 @@ public class TemplateSegments { } public void replaceSegmentAt(int index, int start, int end) { + replaceSegmentAt(index, start, end, false); + } + + public void replaceSegmentAt(int index, int start, int end, boolean preserveGreediness) { RangeMarker rangeMarker = mySegments.get(index); + boolean greedyToLeft = rangeMarker.isGreedyToLeft(); + boolean greedyToRight = rangeMarker.isGreedyToRight(); rangeMarker.dispose(); + Document doc = myEditor.getDocument(); rangeMarker = doc.createRangeMarker(start, end); - rangeMarker.setGreedyToLeft(true); - rangeMarker.setGreedyToRight(true); + rangeMarker.setGreedyToLeft(greedyToLeft || !preserveGreediness); + rangeMarker.setGreedyToRight(greedyToRight || !preserveGreediness); mySegments.set(index, rangeMarker); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java index 846320c4c084..e5f2fd4ae23d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java @@ -234,7 +234,7 @@ public class TemplateSettings implements PersistentStateComponent, Expo } @Nullable - private TemplateImpl getDefaultTemplate(TemplateImpl t) { + public TemplateImpl getDefaultTemplate(TemplateImpl t) { return myDefaultTemplates.get(TemplateKey.keyOf(t)); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java index d3f658cdcf24..47120b38ff79 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java @@ -631,6 +631,8 @@ public class TemplateState implements Disposable { } } + fixOverlappedSegments(myCurrentSegmentNumber); + ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { @@ -682,6 +684,19 @@ public class TemplateState implements Disposable { }); } + private void fixOverlappedSegments(int currentSegment) { + if (currentSegment >= 0) { + int currentSegmentStart = mySegments.getSegmentStart(currentSegment); + int currentSegmentEnd = mySegments.getSegmentEnd(currentSegment); + for (int i = currentSegment + 1; i < mySegments.getSegmentsCount(); i++) { + final int startOffset = mySegments.getSegmentStart(i); + if (currentSegmentStart <= startOffset && startOffset < currentSegmentEnd) { + mySegments.replaceSegmentAt(i, currentSegmentEnd, Math.max(mySegments.getSegmentEnd(i), currentSegmentEnd), true); + } + } + } + } + @NotNull private String getVariableValueText(String variableName) { TextResult value = getVariableValue(variableName); @@ -726,18 +741,13 @@ public class TemplateState implements Disposable { String oldText = myDocument.getCharsSequence().subSequence(start, end).toString(); if (!oldText.equals(newValue)) { - int segmentNumberWithTheSameStart = mySegments.getSegmentWithTheSameStart(segmentNumber, start); mySegments.setNeighboursGreedy(segmentNumber, false); myDocument.replaceString(start, end, newValue); int newEnd = start + newValue.length(); mySegments.replaceSegmentAt(segmentNumber, start, newEnd); mySegments.setNeighboursGreedy(segmentNumber, true); - if (segmentNumberWithTheSameStart != -1) { - mySegments.replaceSegmentAt(segmentNumberWithTheSameStart, newEnd, - newEnd + mySegments.getSegmentEnd(segmentNumberWithTheSameStart) - - mySegments.getSegmentStart(segmentNumberWithTheSameStart)); - } + fixOverlappedSegments(segmentNumber); } } @@ -998,10 +1008,7 @@ public class TemplateState implements Disposable { if (e instanceof MacroCallNode) { marker = ((MacroCallNode)e).getMacro().getDefaultValue(); } - int start = mySegments.getSegmentStart(i); - int end = start + marker.length(); - myDocument.insertString(start, marker); - mySegments.replaceSegmentAt(i, start, end); + replaceString(marker, mySegments.getSegmentStart(i), mySegments.getSegmentEnd(i), i); indices.add(i); break; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/BaseCompleteMacro.java b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/BaseCompleteMacro.java index 96585662d6ad..717724ce30d2 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/BaseCompleteMacro.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/BaseCompleteMacro.java @@ -16,12 +16,14 @@ package com.intellij.codeInsight.template.macro; +import com.intellij.codeInsight.completion.CompletionPhase; +import com.intellij.codeInsight.completion.impl.CompletionServiceImpl; import com.intellij.codeInsight.lookup.*; import com.intellij.codeInsight.template.*; -import com.intellij.codeInsight.template.Result; import com.intellij.codeInsight.template.impl.TemplateManagerImpl; import com.intellij.codeInsight.template.impl.TemplateState; -import com.intellij.openapi.application.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Editor; @@ -81,6 +83,9 @@ public abstract class BaseCompleteMacro extends Macro { CommandProcessor.getInstance().executeCommand(project, new Runnable() { @Override public void run() { + // if we're in some completion's insert handler, make sure our new completion isn't treated as the second invocation + CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); + invokeCompletionHandler(project, editor); Lookup lookup = LookupManager.getInstance(project).getActiveLookup(); 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 3650e188a65e..d3d5d808f871 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java @@ -313,7 +313,7 @@ class FindInProjectTask { // $ is used to separate words when indexing plain-text files but not when indexing // Java identifiers, so we can't consistently break a string containing $ characters into words - return myFindModel.isWholeWordsOnly() && text.indexOf('$') < 0; + return myFindModel.isWholeWordsOnly() && text.indexOf('$') < 0 && !StringUtil.getWordsInStringLongestFirst(text).isEmpty(); } diff --git a/platform/lang-impl/src/com/intellij/openapi/file/exclude/EnforcedPlainTextFileTypeManager.java b/platform/lang-impl/src/com/intellij/openapi/file/exclude/EnforcedPlainTextFileTypeManager.java index e8a18e423423..f40d069c83c1 100644 --- a/platform/lang-impl/src/com/intellij/openapi/file/exclude/EnforcedPlainTextFileTypeManager.java +++ b/platform/lang-impl/src/com/intellij/openapi/file/exclude/EnforcedPlainTextFileTypeManager.java @@ -28,7 +28,7 @@ import com.intellij.openapi.roots.ex.ProjectRootManagerEx; import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileWithId; -import com.intellij.util.containers.ConcurrentHashMap; +import com.intellij.util.containers.ConcurrentWeakHashMap; import com.intellij.util.indexing.FileBasedIndex; import org.jetbrains.annotations.NotNull; @@ -43,7 +43,7 @@ import java.util.Map; * @author Rustam Vishnyakov */ public class EnforcedPlainTextFileTypeManager implements ProjectManagerListener { - private final Map> myPlainTextFileSets = new ConcurrentHashMap>(); + private final Map> myPlainTextFileSets = new ConcurrentWeakHashMap>(); private volatile boolean mySetsInitialized = false; private static final Object LOCK = new Object(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java index bf306856c2c3..2f40df844bb6 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java @@ -318,7 +318,7 @@ public class PsiVFSListener extends VirtualFileAdapter { VirtualFile parent = vFile.getParent(); final PsiDirectory parentDir = getCachedDirectory(parent); - if (FileContentUtilCore.FORCE_RELOAD_REQUESTOR.equals(event.getRequestor())) { + if (oldFileViewProvider != null && FileContentUtilCore.FORCE_RELOAD_REQUESTOR.equals(event.getRequestor())) { forceReload(vFile, oldPsiFile, parentDir); return; } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/IndexingStamp.java b/platform/lang-impl/src/com/intellij/util/indexing/IndexingStamp.java index 79a7cb684486..86ab5750c685 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/IndexingStamp.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/IndexingStamp.java @@ -226,12 +226,12 @@ public class IndexingStamp { } public static void flushCache(@Nullable VirtualFile finishedFile) { - if (finishedFile == null || !ourFinishedFiles.offer(finishedFile)) { + while (finishedFile == null || !ourFinishedFiles.offer(finishedFile)) { List files = new ArrayList(ourFinishedFiles.size()); ourFinishedFiles.drainTo(files); if (!files.isEmpty()) { - for(VirtualFile file:files) { + for (VirtualFile file : files) { synchronized (getStripedLock(file)) { Timestamps timestamp = myTimestampsCache.remove(file); if (timestamp == null) continue; @@ -248,7 +248,8 @@ public class IndexingStamp { } } } - if (finishedFile != null) ourFinishedFiles.offer(finishedFile); + if (finishedFile == null) break; + // else repeat until ourFinishedFiles.offer() succeeds } } diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java index 24a7603e45f7..911b84231ae8 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java @@ -146,6 +146,13 @@ public class IdeaGateway { return nf.getCachedChildren(); } + @NotNull + public static Collection loadAndIterateChildren(VirtualFile f) { + if (!(f instanceof NewVirtualFile)) return Collections.emptyList(); + NewVirtualFile nf = (NewVirtualFile)f; + return Arrays.asList(nf.getChildren()); + } + @NotNull public RootEntry createTransientRootEntry() { ApplicationManager.getApplication().assertReadAccessAllowed(); diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java index cb20957f3ed7..5f9aad057c90 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java @@ -93,7 +93,7 @@ public class LocalHistoryEventDispatcher extends VirtualFileAdapter implements V myVcs.created(f.getPath(), f.isDirectory()); } if (f.isDirectory()) { - for (VirtualFile each : IdeaGateway.iterateDBChildren(f)) { + for (VirtualFile each : IdeaGateway.loadAndIterateChildren(f)) { createRecursively(each); } } diff --git a/platform/platform-api/src/com/intellij/openapi/wm/IdeGlassPaneUtil.java b/platform/platform-api/src/com/intellij/openapi/wm/IdeGlassPaneUtil.java index 67375287e7b2..10d49e44ac48 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/IdeGlassPaneUtil.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/IdeGlassPaneUtil.java @@ -18,7 +18,6 @@ package com.intellij.openapi.wm; import com.intellij.openapi.Disposable; import com.intellij.openapi.ui.Painter; -import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Disposer; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.UiNotifyConnector; @@ -70,9 +69,6 @@ public class IdeGlassPaneUtil { public static boolean canBePreprocessed(MouseEvent e) { Component c = SwingUtilities.getDeepestComponentAt(e.getComponent(), e.getX(), e.getY()); - if (JBPopupFactory.getInstance().getParentBalloonFor(c) != null) { - return false; - } if (c instanceof IdeGlassPane.TopComponent) { return ((IdeGlassPane.TopComponent)c).canBePreprocessed(e); diff --git a/platform/util/src/com/intellij/util/ui/FadeInFadeOut.java b/platform/platform-api/src/com/intellij/util/ui/FadeInFadeOut.java similarity index 79% rename from platform/util/src/com/intellij/util/ui/FadeInFadeOut.java rename to platform/platform-api/src/com/intellij/util/ui/FadeInFadeOut.java index 3e1ec9e6ac90..14eccd0c55f1 100644 --- a/platform/util/src/com/intellij/util/ui/FadeInFadeOut.java +++ b/platform/platform-api/src/com/intellij/util/ui/FadeInFadeOut.java @@ -27,6 +27,7 @@ import java.awt.image.BufferedImage; public class FadeInFadeOut extends JComponent { public static final Integer LAYER = new Integer(JLayeredPane.POPUP_LAYER + 1); + public static final int TOTAL_FRAMES = 10; @NotNull private final Component myComponent; @@ -40,13 +41,14 @@ public class FadeInFadeOut extends JComponent { private final boolean myFadeIn; private boolean myTriggered = false; - private final int myTimeToComplete; private double myRatio = 0; + private final Animator myAnimator; + private Runnable myOnDone = null; + public FadeInFadeOut(@NotNull Component component, int timeToComplete, boolean fadeIn, @NotNull Component icon) { setFocusable(false); myComponent = component; - myTimeToComplete = timeToComplete; myFadeIn = fadeIn; myRatio = myFadeIn ? 0 : 1; myIcon = icon; @@ -55,6 +57,23 @@ public class FadeInFadeOut extends JComponent { Graphics2D graphics = myComponentImage.createGraphics(); myComponent.paint(graphics); graphics.dispose(); + myAnimator = new Animator("FadeInFadeOut", TOTAL_FRAMES, timeToComplete, false) { + @Override + public void paintNow(int frame, int totalFrames, int cycle) { + double linearProgress = Math.max(0, Math.min(1, (double)frame / totalFrames)); + if (!myFadeIn) linearProgress = 1 - linearProgress; + myRatio = (1 - Math.cos(Math.PI * linearProgress)) / 2; + paintImmediately(0, 0, getWidth(), getHeight()); + } + + @Override + protected void paintCycleEnd() { + super.paintCycleEnd(); + if (myOnDone != null) { + myOnDone.run(); + } + } + }; } @Override @@ -66,25 +85,15 @@ public class FadeInFadeOut extends JComponent { } } - public void doAnimation() { + public void doAnimation(final Runnable onDone) { + myOnDone = onDone; if (myTriggered || !isShowing()) return; myTriggered = true; - long startTime = System.currentTimeMillis(); - while (true) { - paintImmediately(0, 0, getWidth(), getHeight()); - final long timeSpent = System.currentTimeMillis() - startTime; - if (timeSpent >= myTimeToComplete) { - return; - } - double linearProgress = Math.max(0, Math.min(1, (double)timeSpent / myTimeToComplete)); - if (!myFadeIn) linearProgress = 1 - linearProgress; - myRatio = (1 - Math.cos(Math.PI * linearProgress)) / 2; - } + myAnimator.resume(); } public final void paint(final Graphics g_) { - long l = System.currentTimeMillis(); int x = (int)(myIconBounds.x * (1 - myRatio) + myComponentBounds.x * myRatio); int y = (int)(myIconBounds.y * (1 - myRatio) + myComponentBounds.y * myRatio); int width = (int)(myIconBounds.width * (1 - myRatio) + myComponentBounds.width * myRatio); diff --git a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java index 53a5918451e0..bf4d92576ee5 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java @@ -384,14 +384,21 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener return UIManager.getFont("ToolTip.font"); } + public boolean hasCurrent() { + return myCurrentTooltip != null; + } + public boolean hideCurrent(@Nullable MouseEvent me, @Nullable AnAction action, @Nullable AnActionEvent event) { return hideCurrent(me, action, event, myCurrentTipUi != null && myCurrentTipUi.isAnimationEnabled()); } public boolean hideCurrent(@Nullable MouseEvent me, @Nullable AnAction action, @Nullable AnActionEvent event, final boolean animationEnabled) { if (myCurrentTooltip != null && me != null && myCurrentTooltip.isInside(RelativePoint.fromScreen(me.getLocationOnScreen()))) { - return false; + if (me.getButton() == MouseEvent.NOBUTTON || myCurrentTipUi == null || myCurrentTipUi.isBlockClicks()) { + return false; + } } + myShowRequest = null; myQueuedComponent = null; myQueuedTooltip = null; @@ -426,7 +433,7 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener } }; - if (me != null) { + if (me != null && me.getButton() == MouseEvent.NOBUTTON) { myAlarm.addRequest(myHideRunnable, Registry.intValue("ide.tooltip.autoDismissDeadZone")); } else { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ExportToTextFileAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ExportToTextFileAction.java index 3cd7df4fb544..0825068722cf 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ExportToTextFileAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ExportToTextFileAction.java @@ -18,9 +18,10 @@ package com.intellij.ide.actions; import com.intellij.ide.ExporterToTextFile; import com.intellij.ide.util.ExportToFileUtil; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -public class ExportToTextFileAction extends AnAction { +public class ExportToTextFileAction extends DumbAwareAction { public void actionPerformed(AnActionEvent e) { DataContext dataContext = e.getDataContext(); Project project = CommonDataKeys.PROJECT.getData(dataContext); diff --git a/platform/platform-impl/src/com/intellij/ide/errorTreeView/NewErrorTreeViewPanel.java b/platform/platform-impl/src/com/intellij/ide/errorTreeView/NewErrorTreeViewPanel.java index 24f7cc3043e4..122c09668f87 100644 --- a/platform/platform-impl/src/com/intellij/ide/errorTreeView/NewErrorTreeViewPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/errorTreeView/NewErrorTreeViewPanel.java @@ -25,6 +25,8 @@ import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.ide.CopyPasteManager; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; @@ -593,7 +595,7 @@ public class NewErrorTreeViewPanel extends JPanel implements DataProvider, Occur return myOccurrenceNavigatorSupport.getPreviousOccurenceActionName(); } - private class RerunAction extends AnAction { + private class RerunAction extends DumbAwareAction { private final Runnable myRerunAction; private final AnAction myCloseAction; @@ -616,7 +618,7 @@ public class NewErrorTreeViewPanel extends JPanel implements DataProvider, Occur } } - private class StopAction extends AnAction { + private class StopAction extends DumbAwareAction { public StopAction() { super(IdeBundle.message("action.stop"), null, AllIcons.Actions.Suspend); } @@ -642,7 +644,7 @@ public class NewErrorTreeViewPanel extends JPanel implements DataProvider, Occur return true; } - private class HideWarningsAction extends ToggleAction { + private class HideWarningsAction extends ToggleAction implements DumbAware { public HideWarningsAction() { super(IdeBundle.message("action.hide.warnings"), null, AllIcons.General.HideWarnings); } diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/AvailablePluginsManagerMain.java b/platform/platform-impl/src/com/intellij/ide/plugins/AvailablePluginsManagerMain.java index 9b62c16fde60..b6dd87c96333 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/AvailablePluginsManagerMain.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/AvailablePluginsManagerMain.java @@ -16,6 +16,9 @@ package com.intellij.ide.plugins; import com.intellij.ide.IdeBundle; +import com.intellij.ide.plugins.sorters.SortByDownloadsAction; +import com.intellij.ide.plugins.sorters.SortByRatingAction; +import com.intellij.ide.plugins.sorters.SortByUpdatedAction; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ComboBoxAction; import com.intellij.openapi.extensions.PluginId; @@ -93,11 +96,6 @@ public class AvailablePluginsManagerMain extends PluginManagerMain { model.setVendor(myVendorFilter); pluginsModel = model; pluginTable = new PluginTable(pluginsModel); - pluginTable.getTableHeader().setReorderingAllowed(false); - //pluginTable.setColumnWidth(PluginManagerColumnInfo.COLUMN_DOWNLOADS, 70); - //pluginTable.setColumnWidth(PluginManagerColumnInfo.COLUMN_DATE, 80); - //pluginTable.setColumnWidth(PluginManagerColumnInfo.COLUMN_RATE, 80); - return ScrollPaneFactory.createScrollPane(pluginTable); } @@ -179,7 +177,7 @@ public class AvailablePluginsManagerMain extends PluginManagerMain { actionGroup.add(new MyFilterCategoryAction()); } else { - actionGroup.add(new SortByStatusAction("Sort Installed First")); + actionGroup.add(createSortersGroup()); actionGroup.add(Separator.getInstance()); actionGroup.add(new ActionInstallPlugin(getAvailable(), getInstalled())); } @@ -198,6 +196,15 @@ public class AvailablePluginsManagerMain extends PluginManagerMain { installed.modifyPluginsList(list); //propagate updates } + @Override + protected DefaultActionGroup createSortersGroup() { + final DefaultActionGroup group = super.createSortersGroup(); + group.addAction(new SortByDownloadsAction(pluginTable, pluginsModel)); + group.addAction(new SortByRatingAction(pluginTable, pluginsModel)); + group.addAction(new SortByUpdatedAction(pluginTable, pluginsModel)); + return group; + } + private class MyFilterCategoryAction extends ComboBoxAction implements DumbAware{ @Override public void update(AnActionEvent e) { diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsManagerMain.java b/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsManagerMain.java index dfd9aa07f381..4c20570f6327 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsManagerMain.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsManagerMain.java @@ -251,7 +251,7 @@ public class InstalledPluginsManagerMain extends PluginManagerMain { //actionGroup.add(new MyFilterBundleAction()); } else { actionGroup.add(new RefreshAction()); - actionGroup.addAction(new SortByStatusAction("Sort by Status")); + actionGroup.addAction(createSortersGroup()); actionGroup.add(Separator.getInstance()); actionGroup.add(new ActionInstallPlugin(getAvailable(), getInstalled())); actionGroup.add(new UninstallPluginAction(this, pluginTable)); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerColumnInfo.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerColumnInfo.java index 591798ae8b70..90150d42aad0 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerColumnInfo.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerColumnInfo.java @@ -97,15 +97,15 @@ class PluginManagerColumnInfo extends ColumnInfo { } protected boolean isSortByName() { - return COLUMN_NAME == columnIdx; + return !isSortByDate() && !isSortByDownloads() && !isSortByStatus(); } protected boolean isSortByDownloads() { - return columnIdx == COLUMN_DOWNLOADS; + return myModel.isSortByDownloads(); } protected boolean isSortByDate() { - return columnIdx == COLUMN_DATE; + return myModel.isSortByUpdated(); } protected boolean isSortByStatus() { @@ -167,76 +167,61 @@ class PluginManagerColumnInfo extends ColumnInfo { } protected Comparator getColumnComparator() { - if (isSortByName()) { - return new Comparator() { - public int compare(IdeaPluginDescriptor o1, IdeaPluginDescriptor o2) { - return StringUtil.compare(o1.getName(), o2.getName(), true); - } - }; - } - if (columnIdx == COLUMN_RATE) { - return new Comparator() { - @Override - public int compare(IdeaPluginDescriptor o1, IdeaPluginDescriptor o2) { + return new Comparator() { + public int compare(IdeaPluginDescriptor o1, IdeaPluginDescriptor o2) { + if (myModel.isSortByRating()) { final String rating1 = ((PluginNode)o1).getRating(); final String rating2 = ((PluginNode)o2).getRating(); - return Comparing.compare(rating1, rating2); + final int compare = Comparing.compare(rating2, rating1); + if (compare != 0) { + return compare; + } } - }; - } - if (isSortByDownloads()) { - return new Comparator() { - public int compare(IdeaPluginDescriptor o1, IdeaPluginDescriptor o2) { + + if (isSortByDate()) { + long date1 = (o1 instanceof PluginNode) ? ((PluginNode)o1).getDate() : ((IdeaPluginDescriptorImpl)o1).getDate(); + long date2 = (o2 instanceof PluginNode) ? ((PluginNode)o2).getDate() : ((IdeaPluginDescriptorImpl)o2).getDate(); + date1 /= 60 * 1000; + date2 /= 60 * 1000; + if (date2 != date1) { + return date2 - date1 > 0L ? 1 : -1; + } + } + + if (isSortByDownloads()) { String count1 = o1.getDownloads(); String count2 = o2.getDownloads(); if (count1 != null && count2 != null) { - return Long.valueOf(count1).compareTo(Long.valueOf(count2)); - } - else if (count1 != null) { - return -1; + final Long result = Long.valueOf(count2); + if (result != 0) { + return result.compareTo(Long.valueOf(count1)); + } } else { - return 1; + return count1 != null ? -1 : 1; } } - }; - } - if (isSortByDate()) { - return new Comparator() { - public int compare(IdeaPluginDescriptor o1, IdeaPluginDescriptor o2) { - long date1 = (o1 instanceof PluginNode) ? ((PluginNode)o1).getDate() : ((IdeaPluginDescriptorImpl)o1).getDate(); - long date2 = (o2 instanceof PluginNode) ? ((PluginNode)o2).getDate() : ((IdeaPluginDescriptorImpl)o2).getDate(); - if (date1 < date2) { - return -1; - } - else if (date1 > date2) return 1; - return 0; - } - }; - } - return new Comparator() { - @Override - public int compare(IdeaPluginDescriptor o1, IdeaPluginDescriptor o2) { - return StringUtil.compare(o1.getCategory(), o2.getCategory(), true); + + return StringUtil.compare(o1.getName(), o2.getName(), true); } }; } - @SuppressWarnings({"HardCodedStringLiteral"}) - public static String getFormattedSize(String size) { - if (size.equals("-1")) { - return IdeBundle.message("plugin.info.unknown"); - } - else if (size.length() >= 4) { - if (size.length() < 7) { - size = String.format("%.1f", (float)Integer.parseInt(size) / kByte) + " K"; + @SuppressWarnings({"HardCodedStringLiteral"}) + public static String getFormattedSize (String size){ + if (size.equals("-1")) { + return IdeBundle.message("plugin.info.unknown"); } - else { - size = String.format("%.1f", (float)Integer.parseInt(size) / mgByte) + " M"; + else if (size.length() >= 4) { + if (size.length() < 7) { + size = String.format("%.1f", (float)Integer.parseInt(size) / kByte) + " K"; + } + else { + size = String.format("%.1f", (float)Integer.parseInt(size) / mgByte) + " M"; + } } + return size; } - return size; - } public static int getRealNodeState(PluginNode node) { if (node.getStatus() == PluginNode.STATUS_DOWNLOADED) return PluginNode.STATUS_DOWNLOADED; diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.java index bf59ca5064ff..476652ae5d18 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.java @@ -19,6 +19,7 @@ import com.intellij.CommonBundle; import com.intellij.icons.AllIcons; import com.intellij.ide.BrowserUtil; import com.intellij.ide.IdeBundle; +import com.intellij.ide.plugins.sorters.SortByStatusAction; import com.intellij.ide.ui.search.SearchUtil; import com.intellij.ide.ui.search.SearchableOptionsRegistrar; import com.intellij.notification.*; @@ -73,7 +74,7 @@ import static com.intellij.openapi.util.text.StringUtil.isEmptyOrSpaces; /** * @author stathik - * @since Dec 25, 2003 + * @author Konstantin Bulenkov */ public abstract class PluginManagerMain implements Disposable { public static Logger LOG = Logger.getInstance("#com.intellij.ide.plugins.PluginManagerMain"); @@ -584,28 +585,6 @@ public abstract class PluginManagerMain implements Disposable { }).notify(project); } - protected class SortByStatusAction extends ToggleAction { - - protected SortByStatusAction(final String title) { - super(title, title, AllIcons.ObjectBrowser.SortByType); - } - - @Override - public boolean isSelected(AnActionEvent e) { - return pluginsModel.isSortByStatus(); - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - IdeaPluginDescriptor[] selected = pluginTable.getSelectedObjects(); - pluginsModel.setSortByStatus(state); - pluginsModel.sort(); - if (selected != null) { - select(selected); - } - } - } - public class MyPluginsFilter extends FilterComponent { public MyPluginsFilter() { @@ -634,4 +613,10 @@ public abstract class PluginManagerMain implements Disposable { e.getPresentation().setEnabled(!myBusy); } } + + protected DefaultActionGroup createSortersGroup() { + final DefaultActionGroup group = new DefaultActionGroup("Sort by", true); + group.addAction(new SortByStatusAction(pluginTable, pluginsModel)); + return group; + } } diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginTable.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginTable.java index 21c2b41bd0ce..c902aa1bd1a7 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginTable.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginTable.java @@ -56,6 +56,7 @@ public class PluginTable extends JBTable { setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); setShowGrid(false); setStriped(true); + this.setTableHeader(null); setTransferHandler(new TransferHandler() { @Nullable @Override diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginTableModel.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginTableModel.java index 8fa37012d6d0..8c5bec007d74 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginTableModel.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginTableModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -22,14 +22,14 @@ import com.intellij.util.ui.SortableColumnModel; import javax.swing.*; import javax.swing.table.AbstractTableModel; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; /** - * Created by IntelliJ IDEA. - * User: stathik - * Date: Dec 26, 2003 - * Time: 4:16:50 PM - * To change this template use Options | File Templates. + * @author stathik + * @author Konstantin Bulenkov */ abstract public class PluginTableModel extends AbstractTableModel implements SortableColumnModel { protected static final String NAME = "Name"; @@ -38,6 +38,9 @@ abstract public class PluginTableModel extends AbstractTableModel implements Sor private RowSorter.SortKey myDefaultSortKey; protected final List filtered = new ArrayList(); private boolean mySortByStatus; + private boolean mySortByRating; + private boolean mySortByDownloads; + private boolean mySortByUpdated; protected PluginTableModel() { } @@ -156,7 +159,7 @@ abstract public class PluginTableModel extends AbstractTableModel implements Sor public abstract boolean isPluginDescriptorAccepted(IdeaPluginDescriptor descriptor); - protected void sort() { + public void sort() { Collections.sort(view, columns[getNameColumn()].getComparator()); fireTableDataChanged(); } @@ -168,7 +171,31 @@ abstract public class PluginTableModel extends AbstractTableModel implements Sor public void setSortByStatus(boolean sortByStatus) { mySortByStatus = sortByStatus; } - + + public boolean isSortByRating() { + return mySortByRating; + } + + public void setSortByRating(boolean sortByRating) { + mySortByRating = sortByRating; + } + + public boolean isSortByDownloads() { + return mySortByDownloads; + } + + public void setSortByDownloads(boolean sortByDownloads) { + mySortByDownloads = sortByDownloads; + } + + public boolean isSortByUpdated() { + return mySortByUpdated; + } + + public void setSortByUpdated(boolean sortByUpdated) { + mySortByUpdated = sortByUpdated; + } + public List getAllPlugins() { final ArrayList list = new ArrayList(); list.addAll(view); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/sorters/AbstractSortByAction.java b/platform/platform-impl/src/com/intellij/ide/plugins/sorters/AbstractSortByAction.java new file mode 100644 index 000000000000..b858a3025e1f --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/plugins/sorters/AbstractSortByAction.java @@ -0,0 +1,55 @@ +/* + * 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.ide.plugins.sorters; + +import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.PluginTable; +import com.intellij.ide.plugins.PluginTableModel; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; + +/** + * @author Konstantin Bulenkov + */ +public abstract class AbstractSortByAction extends ToggleAction { + protected final PluginTable myTable; + protected final PluginTableModel myModel; + + public AbstractSortByAction(String name, PluginTable table, PluginTableModel model) { + super(name, name, null); + myTable = table; + myModel = model; + } + + public abstract boolean isSelected(); + + protected abstract void setSelected(boolean state); + + @Override + public final boolean isSelected(AnActionEvent e) { + return isSelected(); + } + + @Override + public final void setSelected(AnActionEvent e, boolean state) { + IdeaPluginDescriptor[] selected = myTable.getSelectedObjects(); + setSelected(state); + myModel.sort(); + if (selected != null) { + myTable.select(selected); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByDownloadsAction.java b/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByDownloadsAction.java new file mode 100644 index 000000000000..34066c3ddc61 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByDownloadsAction.java @@ -0,0 +1,38 @@ +/* + * 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.ide.plugins.sorters; + +import com.intellij.ide.plugins.PluginTable; +import com.intellij.ide.plugins.PluginTableModel; + +/** + * @author Konstantin Bulenkov + */ +public class SortByDownloadsAction extends AbstractSortByAction { + public SortByDownloadsAction(PluginTable table, PluginTableModel model) { + super("Downloads", table, model); + } + + @Override + public boolean isSelected() { + return myModel.isSortByDownloads(); + } + + @Override + protected void setSelected(boolean state) { + myModel.setSortByDownloads(state); + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByRatingAction.java b/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByRatingAction.java new file mode 100644 index 000000000000..3d63b09f379a --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByRatingAction.java @@ -0,0 +1,38 @@ +/* + * 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.ide.plugins.sorters; + +import com.intellij.ide.plugins.PluginTable; +import com.intellij.ide.plugins.PluginTableModel; + +/** + * @author Konstantin Bulenkov + */ +public class SortByRatingAction extends AbstractSortByAction { + public SortByRatingAction(PluginTable table, PluginTableModel model) { + super("Rating", table, model); + } + + @Override + public boolean isSelected() { + return myModel.isSortByRating(); + } + + @Override + protected void setSelected(boolean state) { + myModel.setSortByRating(state); + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByStatusAction.java b/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByStatusAction.java new file mode 100644 index 000000000000..8092f63468de --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByStatusAction.java @@ -0,0 +1,38 @@ +/* + * 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.ide.plugins.sorters; + +import com.intellij.ide.plugins.PluginTable; +import com.intellij.ide.plugins.PluginTableModel; + +/** +* @author Konstantin Bulenkov +*/ +public class SortByStatusAction extends AbstractSortByAction { + public SortByStatusAction(PluginTable pluginTable, PluginTableModel pluginsModel) { + super("Status", pluginTable, pluginsModel); + } + + @Override + public boolean isSelected() { + return myModel.isSortByStatus(); + } + + @Override + public void setSelected(boolean state) { + myModel.setSortByStatus(state); + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByUpdatedAction.java b/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByUpdatedAction.java new file mode 100644 index 000000000000..c4e3a9dfeaee --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/plugins/sorters/SortByUpdatedAction.java @@ -0,0 +1,38 @@ +/* + * 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.ide.plugins.sorters; + +import com.intellij.ide.plugins.PluginTable; +import com.intellij.ide.plugins.PluginTableModel; + +/** + * @author Konstantin Bulenkov + */ +public class SortByUpdatedAction extends AbstractSortByAction { + public SortByUpdatedAction(PluginTable table, PluginTableModel model) { + super("Last Updated", table, model); + } + + @Override + public boolean isSelected() { + return myModel.isSortByUpdated(); + } + + @Override + protected void setSelected(boolean state) { + myModel.setSortByUpdated(state); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/Change.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/Change.java index 55855a709167..f83bd64ed738 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/Change.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/Change.java @@ -201,16 +201,18 @@ public abstract class Change { myHighlighterHolder = new ChangeHighlighterHolder(); } - public SimpleChangeSide(ChangeSide originalSide, DiffRangeMarker newRange) { + public SimpleChangeSide(@NotNull ChangeSide originalSide, @NotNull DiffRangeMarker newRange) { mySide = ((SimpleChangeSide)originalSide).getFragmentSide(); myRange = newRange; myHighlighterHolder = originalSide.getHighlighterHolder(); } + @NotNull public FragmentSide getFragmentSide() { return mySide; } + @NotNull public DiffRangeMarker getRange() { return myRange; } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeList.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeList.java index bbd8c929c203..84a325553bda 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeList.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeList.java @@ -30,6 +30,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import com.intellij.util.diff.FilesTooBigForDiffException; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -44,7 +45,7 @@ public class ChangeList { private ArrayList myChanges; private ArrayList myAppliedChanges; - public ChangeList(Document base, Document version, Project project) { + public ChangeList(@NotNull Document base, @NotNull Document version, Project project) { myDocuments[0] = base; myDocuments[1] = version; myProject = project; @@ -58,7 +59,7 @@ public class ChangeList { LOG.assertTrue(myListeners.remove(listener)); } - public void setChanges(ArrayList changes) { + public void setChanges(@NotNull ArrayList changes) { if (myChanges != null) { HashSet newChanges = new HashSet(changes); LOG.assertTrue(newChanges.size() == changes.size()); @@ -79,6 +80,7 @@ public class ChangeList { public Project getProject() { return myProject; } + @NotNull public List getChanges() { return new ArrayList(myChanges); } @@ -104,7 +106,8 @@ public class ChangeList { } } - public Document getDocument(FragmentSide side) { + @NotNull + public Document getDocument(@NotNull FragmentSide side) { return myDocuments[side.getIndex()]; } @@ -132,10 +135,10 @@ public class ChangeList { } private abstract static class DiffFragmentsEnumerator { - private final DiffFragment[] myFragments; - private final Context myContext; + @NotNull private final DiffFragment[] myFragments; + @NotNull private final Context myContext; - private DiffFragmentsEnumerator(DiffFragment[] fragments) { + private DiffFragmentsEnumerator(@NotNull DiffFragment[] fragments) { myContext = new Context(); myFragments = fragments; } @@ -158,6 +161,7 @@ public class ChangeList { return StringUtil.countNewLines(text); } + @NotNull protected Context getContext() { return myContext; } @@ -174,15 +178,16 @@ public class ChangeList { return myFragment; } - public int getStart(FragmentSide side) { + public int getStart(@NotNull FragmentSide side) { return myStarts[side.getIndex()]; } - public int getEnd(FragmentSide side) { + public int getEnd(@NotNull FragmentSide side) { return getStart(side) + StringUtil.length(side.getText(myFragment)); } - public TextRange createRange(FragmentSide side) { + @NotNull + public TextRange createRange(@NotNull FragmentSide side) { return new TextRange(getStart(side), getEnd(side)); } } @@ -197,7 +202,7 @@ public class ChangeList { return LineBlocks.fromChanges(changes); } - public void remove(Change change) { + public void remove(@NotNull Change change) { if (change.getType().isApplied()) { LOG.assertTrue(myAppliedChanges.remove(change), change); } @@ -208,7 +213,7 @@ public class ChangeList { fireOnChangeRemoved(); } - public void apply(Change change) { + public void apply(@NotNull Change change) { LOG.assertTrue(myChanges.remove(change), change); myAppliedChanges.add(change); fireOnChangeApplied(); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeSide.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeSide.java index cc2942aeadbb..31b40d8fe913 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeSide.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeSide.java @@ -16,6 +16,7 @@ package com.intellij.openapi.diff.impl.incrementalMerge; import com.intellij.openapi.diff.impl.util.DocumentUtil; +import org.jetbrains.annotations.NotNull; /** * @author Kirill Likhodedov @@ -30,6 +31,7 @@ public abstract class ChangeSide { return DocumentUtil.getStartLine(getRange()); } + @NotNull public String getText() { return DocumentUtil.getText(getRange()); } @@ -38,6 +40,7 @@ public abstract class ChangeSide { return DocumentUtil.getEndLine(getRange()); } + @NotNull public abstract DiffRangeMarker getRange(); public abstract ChangeHighlighterHolder getHighlighterHolder(); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java index 4a9825779d1d..9262a9524f8c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java @@ -148,12 +148,14 @@ public class ChangeType { return highlighter; } + @NotNull static ChangeType fromDiffFragment(DiffFragment fragment) { if (fragment.getText1() == null) return INSERT; if (fragment.getText2() == null) return DELETED; return CHANGE; } + @NotNull static ChangeType fromRanges(@NotNull TextRange left, @NotNull TextRange right) { if (left.getLength() == 0) return INSERT; if (right.getLength() == 0) return DELETED; diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java index fcb7ffe98e0d..172f24e88633 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java @@ -15,106 +15,56 @@ */ package com.intellij.openapi.diff.impl.incrementalMerge; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; -import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.util.TextRange; +import org.jetbrains.annotations.NotNull; /** * One of two conflicting changes. + * * @see MergeConflict */ -class ConflictChange extends Change implements DiffRangeMarker.RangeInvalidListener { +class ConflictChange extends TwoSideChange.SideChange { private static final Logger LOG = Logger.getInstance(ConflictChange.class); - private SimpleChangeSide myOriginalSide; - private MergeConflict myConflict; - private final ChangeList myChangeList; - private ChangeType myType; private boolean mySemiApplied; - public ConflictChange(MergeConflict conflict, FragmentSide mergeSide, TextRange range, ChangeList changeList) { - myConflict = conflict; - myChangeList = changeList; - myOriginalSide = new SimpleChangeSide(mergeSide, new DiffRangeMarker((DocumentEx)conflict.getOriginalDocument(mergeSide), range, this)); - myType = ChangeType.CONFLICT; - } - - @Override - protected void changeSide(ChangeSide sideToChange, DiffRangeMarker newRange) { - myConflict.setRange(newRange); - } - - protected void removeFromList() { - myConflict.conflictRemoved(); - myConflict = null; - } - - public ChangeSide getChangeSide(FragmentSide side) { - return isBranch(side) ? myOriginalSide : myConflict; - } - - private static boolean isBranch(FragmentSide side) { - return MergeList.BRANCH_SIDE == side; - } - - public SimpleChangeSide getOriginalSide() { - return myOriginalSide; - } - - public ChangeType getType() { - return myType; - } - - public ChangeList getChangeList() { - return myConflict.getMergeList().getChanges(myOriginalSide.getFragmentSide()); + public ConflictChange(@NotNull MergeConflict conflict, + @NotNull FragmentSide mergeSide, + @NotNull TextRange versionRange, + @NotNull ChangeList changeList) { + super(conflict, changeList, ChangeType.CONFLICT, mergeSide, versionRange); } @Override public void onApplied() { - myType = ChangeType.deriveApplied(myType); - myChangeList.apply(this); - - myOriginalSide.getHighlighterHolder().updateHighlighter(myOriginalSide, myType); - myOriginalSide.getHighlighterHolder().setActions(new AnAction[0]); - - // display, what one side of the conflict was resolved to - myConflict.getHighlighterHolder().updateHighlighter(myConflict, myType); + markApplied(); // update the other variant of the conflict to point to the bottom if (!mySemiApplied) { - ConflictChange otherChange = myConflict.getOtherChange(this); - LOG.assertTrue(otherChange != null, String.format("Other change is null. This change: %s Merge conflict: %s", this, myConflict)); + ConflictChange otherChange = myTwoSideChange.getOtherChange(this); + LOG.assertTrue(otherChange != null, String.format("Other change is null. This change: %s Merge conflict: %s", this, myTwoSideChange)); otherChange.mySemiApplied = true; otherChange.updateOtherSideOnConflictApply(); - myConflict.removeOtherChange(this); + myTwoSideChange.removeOtherChange(this); } } private void updateOtherSideOnConflictApply() { - int startOffset = myConflict.getRange().getEndOffset(); + if (myOriginalSide.getStart() == myOriginalSide.getEnd()) { + markApplied(); + return; + } + + int startOffset = myTwoSideChange.getRange().getEndOffset(); TextRange emptyRange = new TextRange(startOffset, startOffset); ConflictChange leftChange = isBranch(myOriginalSide.getFragmentSide()) ? null : this; ConflictChange rightChange = isBranch(myOriginalSide.getFragmentSide()) ? this : null; - myConflict = myConflict.deriveSideForNotAppliedChange(emptyRange, leftChange, rightChange); + myTwoSideChange = myTwoSideChange.deriveSideForNotAppliedChange(emptyRange, leftChange, rightChange); myOriginalSide.getHighlighterHolder().updateHighlighter(myOriginalSide, myType); - myConflict.getHighlighterHolder().updateHighlighter(myConflict, myType); + myTwoSideChange.getHighlighterHolder().updateHighlighter(myTwoSideChange, myType); myChangeList.fireOnChangeApplied(); } - - public void onRemovedFromList() { - myOriginalSide.getRange().removeListener(this); - myConflict = null; - myOriginalSide = null; - } - - public boolean isValid() { - return myConflict != null; - } - - public void onRangeInvalidated() { - removeFromList(); - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/DiffRangeMarker.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/DiffRangeMarker.java index 6e6d8665b1d9..53d53505b123 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/DiffRangeMarker.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/DiffRangeMarker.java @@ -23,6 +23,7 @@ import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.impl.RangeMarkerImpl; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -30,7 +31,7 @@ class DiffRangeMarker extends RangeMarkerImpl { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.incrementalMerge.DiffRangeMarker"); private RangeInvalidListener myListener; - DiffRangeMarker(DocumentEx document, TextRange range, RangeInvalidListener listener) { + DiffRangeMarker(@NotNull DocumentEx document, @NotNull TextRange range, RangeInvalidListener listener) { super(document, range.getStartOffset(), range.getEndOffset(),true); myListener = listener; if (myListener != null) InvalidRangeDispatcher.addClient(document); @@ -71,7 +72,7 @@ class DiffRangeMarker extends RangeMarkerImpl { notifier.myDeferedNotifications.add(listener); } - public static void addClient(Document document) { + public static void addClient(@NotNull Document document) { InvalidRangeDispatcher notifier = document.getUserData(KEY); if (notifier == null) { notifier = new InvalidRangeDispatcher(); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeBuilder.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeBuilder.java index a0b526cff493..30672cb3dc56 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeBuilder.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeBuilder.java @@ -18,253 +18,165 @@ package com.intellij.openapi.diff.impl.incrementalMerge; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.diff.impl.util.ContextLogger; import com.intellij.openapi.util.TextRange; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; class MergeBuilder { - private final ContextLogger LOG; - private final int[] myProcessed = new int[]{0, 0, 0}; - private final ArrayList myResult = new ArrayList(); - private final EqualPair[] myPairs = new EqualPair[2]; + @NotNull private final ContextLogger LOG; - public MergeBuilder(ContextLogger log) { + @NotNull private final ArrayList myResult = new ArrayList(); + + @NotNull private final int[] myProcessed = new int[]{0, 0, 0}; // LEFT aka SIDE1, RIGHT aka SIDE2, BASE + @NotNull private final EqualPair[] myPairs = new EqualPair[2]; // LEFT, RIGHT + + public MergeBuilder(@NotNull ContextLogger log) { LOG = log; } - public void add(TextRange base, TextRange version, FragmentSide side) { - LOG.assertTrue(base.getLength() == version.getLength()); - myPairs[side.getIndex()] = new EqualPair(base.getStartOffset(), - version.getStartOffset(), base.getLength(), side, LOG); - if (myPairs[side.otherSide().getIndex()] == null) return; - if (myPairs[0].baseStartFrom(myProcessed) && myPairs[1].baseStartFrom(myProcessed)) { - processInsertOrConflict(); - return; - } - if (processNoIntersection()) return; - for (int i = 0; i < myPairs.length; i++) { - EqualPair pair = myPairs[i]; - if (pair.startsFrom(myProcessed)) { - processDeleteOrChange(FragmentSide.fromIndex(i).otherSide()); - return; - } - } - if (!removeSingleSideBase()) return; - LOG.assertTrue(myPairs[0].getBase() == myPairs[1].getBase()); - LOG.assertTrue(myPairs[0].getBase() > myProcessed[1]); - addMergeFragment(myPairs[0].processVersion(myProcessed), - processBaseChange(myPairs[0]), - myPairs[1].processVersion(myProcessed)); - skipProcessed(); - } - - private boolean processNoIntersection() { - FragmentSide firstSide = getFirstSide(); - int firstIndex = firstSide.getIndex(); - if (myPairs[firstIndex].getBaseEnd() <= myPairs[firstSide.otherSide().getIndex()].getBase()) { - myPairs[firstIndex] = null; - return true; - } - return false; - } - - private boolean removeSingleSideBase() { - FragmentSide firstSide = getFirstSide(); - int firstIndex = firstSide.getIndex(); - int singleSideDelta = myPairs[firstSide.otherSide().getIndex()].getBase() - myPairs[firstIndex].getBase(); - if (singleSideDelta >= myPairs[firstIndex].getLength()) { - LOG.notTested(); - myPairs[firstIndex] = null; - return false; - } - if (!myPairs[firstIndex].cutHead(singleSideDelta)) { - LOG.notTested(); - myPairs[firstIndex] = null; - return false; - } - return true; - } - - private FragmentSide getFirstSide() { - return myPairs[0].getBase() < myPairs[1].getBase() ? FragmentSide.SIDE1 : FragmentSide.SIDE2; - } - - private void processDeleteOrChange(FragmentSide side) { - EqualPair workingPair = myPairs[side.getIndex()]; - EqualPair otherPair = myPairs[side.otherSide().getIndex()]; - LOG.assertTrue(!workingPair.baseStartFrom(myProcessed) && - otherPair.baseStartFrom(myProcessed)); - TextRange versionChange = workingPair.processVersion(myProcessed); - TextRange baseChange = new TextRange(myProcessed[1], workingPair.getBase()); - int changeLength = workingPair.getBase() - myProcessed[1]; - myProcessed[1] += changeLength; - myProcessed[otherPair.getSide().getMergeIndex()] += changeLength; - myProcessed[side.getMergeIndex()] = workingPair.getVersion(); - boolean stillValid = otherPair.cutHead(changeLength); - LOG.assertTrue(stillValid); - myResult.add(MergeFragment.notConflict(baseChange, versionChange, side)); - skipProcessed(); - } - - private TextRange processBaseChange(EqualPair workingPair) { - int base = workingPair.getBase(); - TextRange change = new TextRange(myProcessed[1], base); - int otherBase = myPairs[workingPair.getSide().otherSide().getIndex()].getBase(); - LOG.assertTrue(otherBase >= base); - myProcessed[1] = base; - return change; - } - - private void processInsertOrConflict() { - boolean leftVersionProcessed = myPairs[0].versionStartsFrom(myProcessed); - boolean rightVersionProcessed = myPairs[1].versionStartsFrom(myProcessed); - TextRange emptyBase = new TextRange(myProcessed[1], myProcessed[1]); - if (!leftVersionProcessed && rightVersionProcessed) { - addMergeFragment(myPairs[0].processVersion(myProcessed), emptyBase, null); - } else if (!rightVersionProcessed && leftVersionProcessed) { - addMergeFragment(null, emptyBase, myPairs[1].processVersion(myProcessed)); - } else if (!leftVersionProcessed && !rightVersionProcessed) { - addMergeFragment(myPairs[0].processVersion(myProcessed), emptyBase, myPairs[1].processVersion(myProcessed)); - } - skipProcessed(); - } - - private void addMergeFragment(@Nullable TextRange left, TextRange base, @Nullable TextRange right) { - myResult.add(new MergeFragment(left, base, right)); - } - - private void skipProcessed() { - LOG.assertTrue(myProcessed[0] == myPairs[0].getVersion()); - LOG.assertTrue(myProcessed[2] == myPairs[1].getVersion()); - LOG.assertTrue(myPairs[0].getBase() == myPairs[1].getBase()); - LOG.assertTrue(myPairs[0].getBase() == myProcessed[1]); - int processedDelta = Math.min(myPairs[0].getBaseEnd(), myPairs[1].getBaseEnd()) - myProcessed[1]; - LOG.assertTrue(processedDelta > 0); - for (int i = 0; i < myProcessed.length; i++) myProcessed[i] += processedDelta; - for (int i = 0; i < myPairs.length; i++) - if (!myPairs[i].cutHead(processedDelta)) myPairs[i] = null; - LOG.assertTrue(myPairs[0] == null || myPairs[1] == null); + /* + * ASSERT: all unchanged blocks should be squashed + * add(B1, V1 ...); | -> B2.getStartOffset() > B1.getEndOffset() || V2.getStartOffset() > V1.getEndOffset() + * add(B2, V2 ...); | + */ + public void add(@NotNull TextRange base, @NotNull TextRange version, @NotNull FragmentSide side) { + int index = side.getIndex(); + int otherIndex = side.otherSide().getIndex(); + EqualPair pair = new EqualPair(base, version, side); + + LOG.assertTrue(myPairs[index] == null || pair.getBaseStart() - myPairs[index].getBaseEnd() >= 0); // '==' can be in case of insertion + LOG.assertTrue(myPairs[otherIndex] == null || pair.getBaseStart() >= myPairs[otherIndex].getBaseStart()); + + myPairs[index] = pair; + if (myPairs[otherIndex] != null && myPairs[index].getBaseStart() >= myPairs[otherIndex].getBaseEnd()) myPairs[otherIndex] = null; + + process(); } + @NotNull public List finish(int leftLength, int baseLength, int rightLength) { - int[] lengths = new int[]{ leftLength, baseLength, rightLength }; - if (isProcessedUpto(lengths)) { - return myResult; + if (!compare(new int[]{leftLength, rightLength, baseLength}, myProcessed)) { + processConflict(leftLength, baseLength, rightLength); } - int[] afterEnds = new int[3]; - for (int i = 0; i < lengths.length; i++) { - afterEnds[i] = lengths[i] + 1; - } - FragmentSide notProcessedSide = getNotProcessedSide(); - if (notProcessedSide == null) { - addTailChange(lengths); - return myResult; - } - myPairs[notProcessedSide.getIndex()].grow(1); - FragmentSide processedSide = notProcessedSide.otherSide(); - add(createRange(lengths, afterEnds, 1), createRange(lengths, afterEnds, processedSide.getMergeIndex()), processedSide); - if (!isProcessedUpto(afterEnds)) { - add(createRange(lengths, afterEnds, 1), createRange(lengths, afterEnds, notProcessedSide.getMergeIndex()), notProcessedSide); - } - LOG.assertTrue(isProcessedUpto(afterEnds)); return myResult; } - private static TextRange createRange(int[] starts, int[] ends, int column) { - return new TextRange(starts[column], ends[column]); - } + // see "A Formal Investigation of Diff3" + private void process() { + while (myPairs[0] != null && myPairs[1] != null) { + if (myPairs[0].startsFrom(myProcessed) && myPairs[1].startsFrom(myProcessed)) { + // process stable + int len = Math.min(myPairs[0].getLength(), myPairs[1].getLength()); + if (!myPairs[0].cutHead(len)) { + myPairs[0] = null; + } + if (!myPairs[1].cutHead(len)) { + myPairs[1] = null; + } + myProcessed[0] += len; + myProcessed[1] += len; + myProcessed[2] += len; + } + else { + // process unstable + int nextBase = Math.max(myPairs[0].getBaseStart(), myPairs[1].getBaseStart()); + int[] nextVersion = new int[2]; + nextVersion[0] = nextBase - myPairs[0].getBaseStart() + myPairs[0].getVersionStart(); + nextVersion[1] = nextBase - myPairs[1].getBaseStart() + myPairs[1].getVersionStart(); - private void addTailChange(int[] lengths) { - TextRange[] tailChange = new TextRange[3]; - for (int i = 0; i < tailChange.length; i++) { - if (i != 1 && myProcessed[i] == lengths[i]) tailChange[i] = null; - else tailChange[i] = new TextRange(myProcessed[i], lengths[i]); + processConflict(nextVersion[0], nextBase, nextVersion[1]); + + if (!myPairs[0].cutHead(nextBase - myPairs[0].getBaseStart())) { + myPairs[0] = null; + } + if (!myPairs[1].cutHead(nextBase - myPairs[1].getBaseStart())) { + myPairs[1] = null; + } + } } - myResult.add(new MergeFragment(tailChange[0], tailChange[1], tailChange[2])); } - @Nullable - private FragmentSide getNotProcessedSide() { - EqualPair left = myPairs[0]; - EqualPair right = myPairs[1]; - LOG.assertTrue(left == null || right == null); - if (left != null) return FragmentSide.SIDE1; - if (right != null) return FragmentSide.SIDE2; - return null; + private void processConflict(int nextLeft, int nextBase, int nextRight) { + addConflict(new TextRange(myProcessed[0], nextLeft), + new TextRange(myProcessed[2], nextBase), + new TextRange(myProcessed[1], nextRight)); + + myProcessed[0] = nextLeft; + myProcessed[1] = nextRight; + myProcessed[2] = nextBase; } - private boolean isProcessedUpto(int[] lengths) { + private void addConflict(@NotNull TextRange left, @NotNull TextRange base, @NotNull TextRange right) { + myResult.add(new MergeFragment(left, base, right)); + } + + private static boolean compare(@NotNull int[] lengths, @NotNull int[] processed) { for (int i = 0; i < lengths.length; i++) { - int length = lengths[i]; - if (length != myProcessed[i]) return false; + if (lengths[i] != processed[i]) return false; } return true; } - private static class EqualPair { - private final ContextLogger LOG; - private int myVersionStart; + private class EqualPair { private int myBaseStart; + private int myVersionStart; private int myLength; private final FragmentSide mySide; - public EqualPair(int baseStart, int versionStart, int length, FragmentSide side, ContextLogger log) { - LOG = log; - myBaseStart = baseStart; - myVersionStart = versionStart; - myLength = length; + public EqualPair(@NotNull TextRange base, @NotNull TextRange version, @NotNull FragmentSide side) { + LOG.assertTrue(base.getLength() == version.getLength()); + LOG.assertTrue(base.getLength() > 0); + myBaseStart = base.getStartOffset(); + myVersionStart = version.getStartOffset(); + myLength = base.getLength(); mySide = side; } + public int getBaseStart() { + return myBaseStart; + } + + public int getVersionStart() { + return myVersionStart; + } + + public int getLength() { + return myLength; + } + + public int getBaseEnd() { + return myBaseStart + myLength; + } + + @NotNull + public FragmentSide getSide() { + return mySide; + } + + public int getIndex() { + return mySide.getIndex(); + } + public boolean startsFrom(int[] bound) { return versionStartsFrom(bound) && baseStartFrom(bound); } public boolean versionStartsFrom(int[] bound) { - return myVersionStart == bound[mySide.getMergeIndex()]; + return myVersionStart == bound[mySide.getIndex()]; } public boolean baseStartFrom(int[] bound) { - return myBaseStart == bound[1]; + return myBaseStart == bound[2]; } - public int getBaseEnd() { - return myBaseStart + myLength; - } - - public int getVersion() { - return myVersionStart; - } - - public TextRange processVersion(int[] processed) { - TextRange change = new TextRange(processed[mySide.getMergeIndex()], getVersion()); - processed[mySide.getMergeIndex()] = getVersion(); - return change; - } - - public boolean cutHead(int processedDelta) { - LOG.assertTrue(myLength >= processedDelta); - myBaseStart += processedDelta; - myVersionStart += processedDelta; - myLength -= processedDelta; + public boolean cutHead(int delta) { + LOG.assertTrue(myLength >= delta); + LOG.assertTrue(delta >= 0); + myBaseStart += delta; + myVersionStart += delta; + myLength -= delta; return myLength > 0; } - - public int getBase() { - return myBaseStart; - } - - public int getLength() { - return myLength; - } - - public FragmentSide getSide() { - return mySide; - } - - public void grow(int delta) { - myLength += delta; - } } -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java index 2bab3c00b208..93c046e7fe5c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java @@ -16,8 +16,6 @@ package com.intellij.openapi.diff.impl.incrementalMerge; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.util.TextRange; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,111 +23,33 @@ import org.jetbrains.annotations.Nullable; /** * Represents a merge conflict, i.e. two {@link ConflictChange conflicting changes}, one from left, another from right. */ -class MergeConflict extends ChangeSide implements DiffRangeMarker.RangeInvalidListener { - - @NotNull private final MergeList myMergeList; - @NotNull private DiffRangeMarker myCommonRange; - @Nullable private ConflictChange myLeftChange; - @Nullable private ConflictChange myRightChange; - @NotNull private final ChangeHighlighterHolder myCommonHighlighterHolder; - - MergeConflict(TextRange commonRange, MergeList mergeList, TextRange leftMarker, TextRange rightMarker) { - myCommonRange = new DiffRangeMarker((DocumentEx)mergeList.getBaseDocument(),commonRange, this); - myMergeList = mergeList; - myCommonHighlighterHolder = new ChangeHighlighterHolder(); - myLeftChange = new ConflictChange(this, FragmentSide.SIDE1, leftMarker, mergeList.getLeftChangeList()); - myRightChange = new ConflictChange(this, FragmentSide.SIDE2, rightMarker, mergeList.getRightChangeList()); +class MergeConflict extends TwoSideChange { + MergeConflict(@NotNull TextRange baseRange, + @NotNull TextRange leftRange, + @NotNull TextRange rightRange, + @NotNull MergeList mergeList) { + super(baseRange, mergeList, new ChangeHighlighterHolder()); + myLeftChange = new ConflictChange(this, FragmentSide.SIDE1, leftRange, mergeList.getLeftChangeList()); + myRightChange = new ConflictChange(this, FragmentSide.SIDE2, rightRange, mergeList.getRightChangeList()); } - private MergeConflict(TextRange commonRange, MergeList mergeList, ChangeHighlighterHolder highlighterHolder, - ConflictChange leftChange, ConflictChange rightChange) { - myCommonRange = new DiffRangeMarker((DocumentEx)mergeList.getBaseDocument(),commonRange, this); - myMergeList = mergeList; - myCommonHighlighterHolder = highlighterHolder; + private MergeConflict(@NotNull TextRange baseRange, + @Nullable ConflictChange leftChange, + @Nullable ConflictChange rightChange, + @NotNull MergeList mergeList, + @NotNull ChangeHighlighterHolder highlighterHolder) { + super(baseRange, mergeList, highlighterHolder); myLeftChange = leftChange; myRightChange = rightChange; } - public MergeConflict deriveSideForNotAppliedChange(TextRange baseRange, @Nullable ConflictChange leftChange, + @NotNull + public MergeConflict deriveSideForNotAppliedChange(@NotNull TextRange baseRange, + @Nullable ConflictChange leftChange, @Nullable ConflictChange rightChange) { ChangeHighlighterHolder highlighterHolder = new ChangeHighlighterHolder(); - MergeConflict mergeConflict = new MergeConflict(baseRange, myMergeList, highlighterHolder, leftChange, rightChange); + MergeConflict mergeConflict = new MergeConflict(baseRange, leftChange, rightChange, myMergeList, highlighterHolder); highlighterHolder.highlight(mergeConflict, myCommonHighlighterHolder.getEditor(), ChangeType.CONFLICT); return mergeConflict; } - - public ChangeHighlighterHolder getHighlighterHolder() { - return myCommonHighlighterHolder; - } - - public DiffRangeMarker getRange() { - return myCommonRange; - } - - @Nullable - public ConflictChange getLeftChange() { - return myLeftChange; - } - - @Nullable - public ConflictChange getRightChange() { - return myRightChange; - } - - @Nullable - ConflictChange getOtherChange(ConflictChange change) { - if (change == myLeftChange) { - return myRightChange; - } - else if (change == myRightChange) { - return myLeftChange; - } - else { - throw new IllegalStateException("Unexpected change: " + change); - } - } - - public void conflictRemoved() { - removeHighlighters(myLeftChange); - removeHighlighters(myRightChange); - myCommonHighlighterHolder.removeHighlighters(); - myMergeList.removeChanges(myLeftChange, myRightChange); - myCommonRange.removeListener(this); - } - - public void setRange(DiffRangeMarker range) { - myCommonRange = range; - } - - public void removeOtherChange(ConflictChange change) { - if (change == myLeftChange) { - myRightChange = null; - } - else if (change == myRightChange) { - myLeftChange = null; - } - else { - throw new IllegalStateException("Unexpected change: " + change); - } - } - - private static void removeHighlighters(@Nullable ConflictChange change) { - if (change != null) { - change.getOriginalSide().getHighlighterHolder().removeHighlighters(); - } - } - - public Document getOriginalDocument(FragmentSide mergeSide) { - return myMergeList.getChanges(mergeSide).getDocument(MergeList.BRANCH_SIDE); - } - - public void onRangeInvalidated() { - conflictRemoved(); - } - - @NotNull - public MergeList getMergeList() { - return myMergeList; - } - } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeFragment.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeFragment.java index 91096b5d4097..f0ec11068ca0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeFragment.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeFragment.java @@ -15,47 +15,22 @@ */ package com.intellij.openapi.diff.impl.incrementalMerge; -import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.util.TextRange; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -/** - * Represents one fragment in the 3-way merge, i.e. an insertion, a deletion, a modification or a conflict. - * Holds information about the {@link TextRange TextRanges} of changed fragments.
- * One and only one left or right fragment is null for all cases, except for the conflict, when both are not null. - */ final class MergeFragment { - @Nullable private final TextRange myLeft; + @NotNull private final TextRange myLeft; @NotNull private final TextRange myBase; - @Nullable private final TextRange myRight; + @NotNull private final TextRange myRight; - MergeFragment(@Nullable TextRange left, @NotNull TextRange base, @Nullable TextRange right) { + MergeFragment(@NotNull TextRange left, @NotNull TextRange base, @NotNull TextRange right) { myLeft = left; myBase = base; myRight = right; } - /** - * Creates a non-conflicting MergeFragment, given one of TextRanges and the FragmentSide to identify which range is it: right or left. - */ @NotNull - static MergeFragment notConflict(@NotNull TextRange baseChange, @NotNull TextRange versionChange, @NotNull FragmentSide versionSide) { - TextRange left; - TextRange right; - if (versionSide == FragmentSide.SIDE1) { - left = versionChange; - right = null; - } - else { - left = null; - right = versionChange; - } - return new MergeFragment(left, baseChange, right); - } - - @Nullable TextRange getLeft() { return myLeft; } @@ -65,7 +40,7 @@ final class MergeFragment { return myBase; } - @Nullable + @NotNull TextRange getRight() { return myRight; } @@ -77,38 +52,21 @@ final class MergeFragment { MergeFragment fragment = (MergeFragment)o; - // suppressing unnecessary check for null, because myBase may occasionally become nullable - //noinspection ConstantConditions - if (myBase != null ? !myBase.equals(fragment.myBase) : fragment.myBase != null) return false; - if (myLeft != null ? !myLeft.equals(fragment.myLeft) : fragment.myLeft != null) return false; - if (myRight != null ? !myRight.equals(fragment.myRight) : fragment.myRight != null) return false; - + if (!myBase.equals(fragment.myBase)) return false; + if (!myLeft.equals(fragment.myLeft)) return false; + if (!myRight.equals(fragment.myRight)) return false; return true; } @Override public int hashCode() { - int result = myLeft != null ? myLeft.hashCode() : 0; - // suppressing unnecessary check for null, because myBase may occasionally become nullable - // noinspection ConstantConditions - result = 31 * result + (myBase != null ? myBase.hashCode() : 0); - result = 31 * result + (myRight != null ? myRight.hashCode() : 0); + int result = myLeft.hashCode(); + result = 31 * result + myBase.hashCode(); + result = 31 * result + myRight.hashCode(); return result; } public String toString() { - StringBuilder buffer = new StringBuilder(); - buffer.append("<"); - buffer.append(range2String(myLeft)).append(", "); - buffer.append(range2String(myBase)).append(", "); - buffer.append(range2String(myRight)); - buffer.append(">"); - return buffer.toString(); + return "<" + myLeft.toString() + ", " + myBase.toString() + ", " + myRight.toString() + ">"; } - - @NotNull - private static String range2String(@Nullable TextRange left) { - return left != null ? left.toString() : "-----"; - } - } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java index d3a0b5a50d58..5f7141d9b4d0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java @@ -24,11 +24,11 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.DiffBundle; import com.intellij.openapi.diff.DiffContent; import com.intellij.openapi.diff.DiffRequest; -import com.intellij.openapi.diff.impl.string.DiffString; import com.intellij.openapi.diff.ex.DiffFragment; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.diff.impl.incrementalMerge.ui.MergePanel2; import com.intellij.openapi.diff.impl.processing.DiffPolicy; +import com.intellij.openapi.diff.impl.string.DiffString; import com.intellij.openapi.diff.impl.util.ContextLogger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; @@ -108,32 +108,26 @@ public class MergeList implements UserDataHolder { ArrayList leftChanges = new ArrayList(); ArrayList rightChanges = new ArrayList(); - for (Iterator fragmentIterator = fragmentList.iterator(); fragmentIterator.hasNext(); ) { - final MergeFragment mergeFragment = fragmentIterator.next(); + for (MergeFragment mergeFragment : fragmentList) { TextRange baseRange = mergeFragment.getBase(); TextRange leftRange = mergeFragment.getLeft(); TextRange rightRange = mergeFragment.getRight(); - if (leftRange == null) { - if (rightRange == null) { - if (!fragmentIterator.hasNext() && baseRange.getEndOffset() == baseText.length()) { - // the very end, both local and remote revisions does not contain latest base fragment - final int rightTextLength = rightText.length(); - final int leftTextLength = leftText.length(); - rightChanges.add(SimpleChange.fromRanges(baseRange, new TextRange(rightTextLength, rightTextLength), mergeList.myBaseToRightChangeList)); - leftChanges.add(SimpleChange.fromRanges(baseRange, new TextRange(leftTextLength, leftTextLength), mergeList.myBaseToLeftChangeList)); - } else { - LOG.error("Left Text: " + leftText + "\n" + "Right Text: " + rightText + "\nBase Text: " + baseText); - } - } else { - rightChanges.add(SimpleChange.fromRanges(baseRange, rightRange, mergeList.myBaseToRightChangeList)); - } + if (compareSubstring(leftText, leftRange, rightText, rightRange)) { + MergeNoConflict conflict = new MergeNoConflict(baseRange, leftRange, rightRange, mergeList); + assert conflict.getLeftChange() != null; + assert conflict.getRightChange() != null; + leftChanges.add(conflict.getLeftChange()); + rightChanges.add(conflict.getRightChange()); } - else if (rightRange == null) { + else if (compareSubstring(baseText, baseRange, leftText, leftRange)) { + rightChanges.add(SimpleChange.fromRanges(baseRange, rightRange, mergeList.myBaseToRightChangeList)); + } + else if (compareSubstring(baseText, baseRange, rightText, rightRange)) { leftChanges.add(SimpleChange.fromRanges(baseRange, leftRange, mergeList.myBaseToLeftChangeList)); } else { - MergeConflict conflict = new MergeConflict(baseRange, mergeList, leftRange, rightRange); + MergeConflict conflict = new MergeConflict(baseRange, leftRange, rightRange, mergeList); assert conflict.getLeftChange() != null; assert conflict.getRightChange() != null; leftChanges.add(conflict.getLeftChange()); @@ -145,7 +139,23 @@ public class MergeList implements UserDataHolder { return mergeList; } - @Nullable + private static boolean compareSubstring(@NotNull String text1, + @NotNull TextRange range1, + @NotNull String text2, + @NotNull TextRange range2) { + if (range1.getLength() != range2.getLength()) return false; + + int index1 = range1.getStartOffset(); + int index2 = range2.getStartOffset(); + while (index1 < range1.getEndOffset()) { + if (text1.charAt(index1) != text2.charAt(index2)) return false; + index1++; + index2++; + } + return true; + } + + @NotNull private static List processText(@NotNull String leftText, @NotNull String baseText, @NotNull String rightText, diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeNoConflict.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeNoConflict.java new file mode 100644 index 000000000000..7c53f9ef1eb0 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeNoConflict.java @@ -0,0 +1,16 @@ +package com.intellij.openapi.diff.impl.incrementalMerge; + +import com.intellij.openapi.diff.impl.highlighting.FragmentSide; +import com.intellij.openapi.util.TextRange; +import org.jetbrains.annotations.NotNull; + +public class MergeNoConflict extends TwoSideChange { + MergeNoConflict(@NotNull TextRange baseRange, + @NotNull TextRange leftRange, + @NotNull TextRange rightRange, + @NotNull MergeList mergeList) { + super(baseRange, mergeList, new ChangeHighlighterHolder()); + myLeftChange = new NoConflictChange(this, FragmentSide.SIDE1, baseRange, leftRange, mergeList.getLeftChangeList()); + myRightChange = new NoConflictChange(this, FragmentSide.SIDE2, baseRange, rightRange, mergeList.getRightChangeList()); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/NoConflictChange.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/NoConflictChange.java new file mode 100644 index 000000000000..9cd475765952 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/NoConflictChange.java @@ -0,0 +1,37 @@ +package com.intellij.openapi.diff.impl.incrementalMerge; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.diff.impl.highlighting.FragmentSide; +import com.intellij.openapi.util.TextRange; +import org.jetbrains.annotations.NotNull; + +public class NoConflictChange extends TwoSideChange.SideChange { + private static final Logger LOG = Logger.getInstance(NoConflictChange.class); + + private boolean myApplied; + + public NoConflictChange(@NotNull MergeNoConflict twoSideChange, + @NotNull FragmentSide mergeSide, + @NotNull TextRange baseRange, + @NotNull TextRange versionRange, + @NotNull ChangeList changeList) { + super(twoSideChange, changeList, ChangeType.fromRanges(baseRange, versionRange), mergeSide, versionRange); + } + + @Override + public void onApplied() { + markApplied(); + + NoConflictChange otherChange = myTwoSideChange.getOtherChange(this); + LOG.assertTrue(otherChange != null, String.format("Other change is null. This change: %s Merge conflict: %s", this, myTwoSideChange)); + otherChange.markApplied(); + } + + @Override + protected void markApplied() { + if (!myApplied) { + myApplied = true; + super.markApplied(); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/SimpleChange.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/SimpleChange.java index 7432f1438975..115db170f617 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/SimpleChange.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/SimpleChange.java @@ -28,14 +28,14 @@ class SimpleChange extends Change implements DiffRangeMarker.RangeInvalidListene private final SimpleChangeSide[] mySides; private final ChangeList myChangeList; - public SimpleChange(ChangeType type, @NotNull TextRange range1, @NotNull TextRange range2, ChangeList changeList) { - mySides = new SimpleChangeSide[]{createSide(changeList, range1, FragmentSide.SIDE1), - createSide(changeList, range2, FragmentSide.SIDE2)}; + public SimpleChange(@NotNull ChangeType type, @NotNull TextRange range1, @NotNull TextRange range2, @NotNull ChangeList changeList) { + mySides = new SimpleChangeSide[]{ createSide(changeList, range1, FragmentSide.SIDE1), + createSide(changeList, range2, FragmentSide.SIDE2)}; myType = type; myChangeList = changeList; } - private SimpleChangeSide createSide(ChangeList changeList, TextRange range1, FragmentSide side) { + private SimpleChangeSide createSide(@NotNull ChangeList changeList, @NotNull TextRange range1, @NotNull FragmentSide side) { return new SimpleChangeSide(side, new DiffRangeMarker((DocumentEx)changeList.getDocument(side), range1, this)); } @@ -45,7 +45,7 @@ class SimpleChange extends Change implements DiffRangeMarker.RangeInvalidListene * @param newRange New change range. */ @Override - protected void changeSide(ChangeSide sideToChange, DiffRangeMarker newRange) { + protected void changeSide(@NotNull ChangeSide sideToChange, @NotNull DiffRangeMarker newRange) { for (int i = 0; i < mySides.length; i++) { SimpleChangeSide side = mySides[i]; if (side.equals(sideToChange)) { @@ -59,7 +59,8 @@ class SimpleChange extends Change implements DiffRangeMarker.RangeInvalidListene myChangeList.remove(this); } - public ChangeSide getChangeSide(FragmentSide side) { + @NotNull + public ChangeSide getChangeSide(@NotNull FragmentSide side) { return mySides[side.getIndex()]; } @@ -100,7 +101,7 @@ class SimpleChange extends Change implements DiffRangeMarker.RangeInvalidListene myChangeList.remove(this); } - public static Change fromRanges(@NotNull TextRange baseRange, @NotNull TextRange versionRange, ChangeList changeList) { + public static Change fromRanges(@NotNull TextRange baseRange, @NotNull TextRange versionRange, @NotNull ChangeList changeList) { ChangeType type = ChangeType.fromRanges(baseRange, versionRange); return new SimpleChange(type, baseRange, versionRange, changeList); } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/TwoSideChange.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/TwoSideChange.java new file mode 100644 index 000000000000..aecc0294d4e7 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/TwoSideChange.java @@ -0,0 +1,181 @@ +package com.intellij.openapi.diff.impl.incrementalMerge; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.diff.impl.highlighting.FragmentSide; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.ex.DocumentEx; +import com.intellij.openapi.util.TextRange; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class TwoSideChange extends ChangeSide implements DiffRangeMarker.RangeInvalidListener { + @NotNull protected final MergeList myMergeList; + @NotNull protected DiffRangeMarker myBaseRangeMarker; + protected T myLeftChange; + protected T myRightChange; + @NotNull protected final ChangeHighlighterHolder myCommonHighlighterHolder; + + protected TwoSideChange(@NotNull TextRange baseRange, + @NotNull MergeList mergeList, + @NotNull ChangeHighlighterHolder highlighterHolder) { + myBaseRangeMarker = new DiffRangeMarker((DocumentEx)mergeList.getBaseDocument(), baseRange, this); + myMergeList = mergeList; + myCommonHighlighterHolder = highlighterHolder; + } + + @NotNull + public ChangeHighlighterHolder getHighlighterHolder() { + return myCommonHighlighterHolder; + } + + @NotNull + public DiffRangeMarker getRange() { + return myBaseRangeMarker; + } + + @Nullable + public Change getLeftChange() { + return myLeftChange; + } + + @Nullable + public Change getRightChange() { + return myRightChange; + } + + public void setRange(@NotNull DiffRangeMarker range) { + myBaseRangeMarker = range; + } + + @Nullable + T getOtherChange(@NotNull T change) { + if (change == myLeftChange) { + return myRightChange; + } + else if (change == myRightChange) { + return myLeftChange; + } + else { + throw new IllegalStateException("Unexpected change: " + change); + } + } + + public void removeOtherChange(@NotNull T change) { + if (change == myLeftChange) { + myRightChange = null; + } + else if (change == myRightChange) { + myLeftChange = null; + } + else { + throw new IllegalStateException("Unexpected change: " + change); + } + } + + public void conflictRemoved() { + removeHighlighters(myLeftChange); + removeHighlighters(myRightChange); + myCommonHighlighterHolder.removeHighlighters(); + myMergeList.removeChanges(myLeftChange, myRightChange); + myBaseRangeMarker.removeListener(this); + } + + private static void removeHighlighters(@Nullable T change) { + if (change != null) { + change.getOriginalSide().getHighlighterHolder().removeHighlighters(); + } + } + + @NotNull + public Document getOriginalDocument(FragmentSide mergeSide) { + return myMergeList.getChanges(mergeSide).getDocument(MergeList.BRANCH_SIDE); + } + + public void onRangeInvalidated() { + conflictRemoved(); + } + + @NotNull + public MergeList getMergeList() { + return myMergeList; + } + + protected static abstract class SideChange extends Change implements DiffRangeMarker.RangeInvalidListener { + protected V myTwoSideChange; + @NotNull protected final ChangeList myChangeList; + + protected SimpleChangeSide myOriginalSide; + @NotNull protected ChangeType myType; + + protected SideChange(@NotNull V twoSideChange, + @NotNull ChangeList changeList, + @NotNull ChangeType type, + @NotNull FragmentSide mergeSide, + @NotNull TextRange versionRange) { + myTwoSideChange = twoSideChange; + myChangeList = changeList; + myOriginalSide = + new SimpleChangeSide(mergeSide, new DiffRangeMarker((DocumentEx)twoSideChange.getOriginalDocument(mergeSide), versionRange, this)); + myType = type; + } + + @NotNull + public ChangeType getType() { + return myType; + } + + public SimpleChangeSide getOriginalSide() { + return myOriginalSide; + } + + protected void markApplied() { + myType = ChangeType.deriveApplied(myType); + myChangeList.apply(this); + + myOriginalSide.getHighlighterHolder().updateHighlighter(myOriginalSide, myType); + myOriginalSide.getHighlighterHolder().setActions(new AnAction[0]); + + // display, what one side of the conflict was resolved to + myTwoSideChange.getHighlighterHolder().updateHighlighter(myTwoSideChange, myType); + } + + public ChangeList getChangeList() { + return myTwoSideChange.getMergeList().getChanges(myOriginalSide.getFragmentSide()); + } + + @Override + protected void changeSide(ChangeSide sideToChange, DiffRangeMarker newRange) { + myTwoSideChange.setRange(newRange); + } + + @NotNull + @Override + public ChangeSide getChangeSide(@NotNull FragmentSide side) { + return isBranch(side) ? myOriginalSide : myTwoSideChange; + } + + protected static boolean isBranch(@NotNull FragmentSide side) { + return MergeList.BRANCH_SIDE == side; + } + + protected void removeFromList() { + myTwoSideChange.conflictRemoved(); + myTwoSideChange = null; + } + + public boolean isValid() { + return myTwoSideChange != null; + } + + public void onRemovedFromList() { + myOriginalSide.getRange().removeListener(this); + myTwoSideChange = null; + myOriginalSide = null; + } + + public void onRangeInvalidated() { + removeFromList(); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DocumentUtil.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DocumentUtil.java index 3b7545f36dd8..1bf5ab2d04a1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DocumentUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DocumentUtil.java @@ -21,7 +21,8 @@ import org.jetbrains.annotations.NotNull; public class DocumentUtil { - public static String getText(RangeMarker range) { + @NotNull + public static String getText(@NotNull RangeMarker range) { return range.getDocument().getText().substring(range.getStartOffset(), range.getEndOffset()); } @@ -29,14 +30,14 @@ public class DocumentUtil { return rangeMarker.getStartOffset() == rangeMarker.getEndOffset(); } - public static int getStartLine(RangeMarker range) { + public static int getStartLine(@NotNull RangeMarker range) { final Document doc = range.getDocument(); if (doc.getTextLength() == 0) return 0; return doc.getLineNumber(range.getStartOffset()); } - public static int getEndLine(RangeMarker range) { + public static int getEndLine(@NotNull RangeMarker range) { Document document = range.getDocument(); int endOffset = range.getEndOffset(); @@ -47,11 +48,11 @@ public class DocumentUtil { return endLine; } - private static boolean lastLineIsNotEmpty(Document document, int line) { + private static boolean lastLineIsNotEmpty(@NotNull Document document, int line) { return document.getTextLength() != document.getLineStartOffset(line); } - public static int getLength(RangeMarker rangeMarker) { + public static int getLength(@NotNull RangeMarker rangeMarker) { return rangeMarker.getEndOffset() - rangeMarker.getStartOffset(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 8b4192947be8..e1e5676eb9db 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -722,8 +722,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi repaintToScreenBottom(getCaretModel().getLogicalPosition().line); int y = getCaretModel().getVisualLineStart() * getLineHeight(); myGutterComponent.repaint(0, y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - y); - getCaretModel().moveToOffset(getCaretModel().getOffset()); } + // make sure carets won't appear at invalid positions (e.g. on Tab width change) + getCaretModel().runForEachCaret(new CaretAction() { + @Override + public void perform(Caret caret) { + caret.moveToOffset(caret.getOffset()); + } + }); } private void initTabPainter() { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java index 7819584e3990..a142a908cb22 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java @@ -852,7 +852,7 @@ public class EditorWindow { final int index = findEditorIndex(findFileComposite(file)); if (index != -1) { setTitleAt(index, EditorTabbedContainer.calcTabTitle(getManager().getProject(), file)); - setToolTipTextAt(index, getManager().getFileTooltipText(file)); + setToolTipTextAt(index, UISettings.getInstance().SHOW_TABS_TOOLTIPS ? getManager().getFileTooltipText(file) : null); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/TrailingSpacesStripper.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/TrailingSpacesStripper.java index b4cc5fe9a240..a5c477facfb3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/TrailingSpacesStripper.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/TrailingSpacesStripper.java @@ -31,6 +31,7 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.util.ShutDownTracker; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.util.ArrayUtil; import com.intellij.util.text.CharArrayUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -133,13 +134,18 @@ public final class TrailingSpacesStripper extends FileDocumentManagerAdapter { final boolean doStrip = !stripTrailingSpaces.equals(EditorSettingsExternalizable.STRIP_TRAILING_SPACES_NONE); final boolean inChangedLinesOnly = !stripTrailingSpaces.equals(EditorSettingsExternalizable.STRIP_TRAILING_SPACES_WHOLE); - List caretLines = new ArrayList(); + int[] caretLines; if (activeEditor != null && inChangedLinesOnly && doStrip && !isVirtualSpaceEnabled) { List carets = activeEditor.getCaretModel().getAllCarets(); - for (Caret caret : carets) { - caretLines.add(caret.getLogicalPosition().line); + caretLines = new int[carets.size()]; + for (int i = 0; i < carets.size(); i++) { + Caret caret = carets.get(i); + caretLines[i] = caret.getLogicalPosition().line; } } + else { + caretLines = ArrayUtil.EMPTY_INT_ARRAY; + } ((DocumentImpl)document).clearLineModificationFlagsExcept(caretLines); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java index 7719e784a4ee..bfbfe5c65836 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java @@ -23,6 +23,8 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.ui.Painter; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.ui.impl.GlassPaneDialogWrapperPeer; +import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.IdeGlassPane; import com.intellij.openapi.wm.IdeGlassPaneUtil; @@ -266,6 +268,8 @@ public class IdeGlassPaneImpl extends JPanel implements IdeGlassPaneEx, IdeEvent return false; } + private MouseEvent myLastRedispatchedEvent = null; + private boolean preprocess(final MouseEvent e, final boolean motion, JRootPane eventRootPane) { try { final MouseEvent event = convertEvent(e, eventRootPane); @@ -274,6 +278,25 @@ public class IdeGlassPaneImpl extends JPanel implements IdeGlassPaneEx, IdeEvent return false; } + Component c = SwingUtilities.getDeepestComponentAt(e.getComponent(), e.getX(), e.getY()); + Balloon balloon = JBPopupFactory.getInstance().getParentBalloonFor(c); + if (balloon != null && myLastRedispatchedEvent != e) { + if (e.getID() == MouseEvent.MOUSE_PRESSED + && IdeTooltipManager.getInstance().hasCurrent() + && IdeTooltipManager.getInstance().hideCurrent(event, null, null, false)) { + //noinspection SSBasedInspection + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + myLastRedispatchedEvent = e; + IdeEventQueue.getInstance().dispatchEvent(e); + } + }); + } + return false; + } + myLastRedispatchedEvent = null; + for (EventListener each : myMouseListeners) { if (motion && each instanceof MouseMotionListener) { fireMouseMotion((MouseMotionListener)each, event); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java index 49e101aaf0d1..83ddb7e65551 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java @@ -860,11 +860,16 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable { add(fadeIn, FadeInFadeOut.LAYER); fadeIn.setBounds(0, 0, getWidth(), getHeight()); myLayeredPane.remove(myComponent); - fadeIn.doAnimation(); - remove(fadeIn); - myLayeredPane.add(myComponent, JLayeredPane.PALETTE_LAYER); - repaint(); - finish(); + fadeIn.doAnimation(new Runnable() { + @Override + public void run() { + remove(fadeIn); + myLayeredPane.add(myComponent, JLayeredPane.PALETTE_LAYER); + //myComponent.requestFocus(); + repaint(); + finish(); + } + }); } else { // not animated myLayeredPane.add(myComponent, JLayeredPane.PALETTE_LAYER); @@ -1037,10 +1042,14 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable { add(fadeOut, FadeInFadeOut.LAYER); fadeOut.setBounds(0, 0, getWidth(), getHeight()); myLayeredPane.remove(myComponent); - fadeOut.doAnimation(); - remove(fadeOut); - repaint(); - finish(); + fadeOut.doAnimation(new Runnable() { + @Override + public void run() { + remove(fadeOut); + repaint(); + finish(); + } + }); } else { // not animated myLayeredPane.remove(myComponent); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInToolWindowCmd.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInToolWindowCmd.java index 177e73efe9c2..cb1ee93e394f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInToolWindowCmd.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInToolWindowCmd.java @@ -23,11 +23,11 @@ import com.intellij.openapi.wm.FocusWatcher; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy; -import com.intellij.openapi.wm.ex.ToolWindowEx; import com.intellij.openapi.wm.impl.FloatingDecorator; import com.intellij.openapi.wm.impl.ToolWindowImpl; import com.intellij.openapi.wm.impl.WindowManagerImpl; import com.intellij.openapi.wm.impl.WindowWatcher; +import com.intellij.util.Alarm; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -71,6 +71,7 @@ public final class RequestFocusInToolWindowCmd extends FinalizableCommand { if (myTimestamp.isExpired()) { return; } + System.out.println("processRequestFocus"); Component preferredFocusedComponent = myToolWindow.isUseLastFocusedOnActivation() ? myFocusWatcher.getFocusedComponent() : null; @@ -154,28 +155,42 @@ public final class RequestFocusInToolWindowCmd extends FinalizableCommand { private ActionCallback requestFocus(final Component c) { - ActionCallback result = new ActionCallback(); - final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); - if (owner != null && owner == c) { - myManager.getFocusManager().requestFocus(new FocusCommand() { - @NotNull - public ActionCallback run() { - return new ActionCallback.Done(); + final ActionCallback result = new ActionCallback(); + final Alarm checkerAlarm = new Alarm(result); + Runnable checker = new Runnable() { + final long startTime = System.currentTimeMillis(); + @Override + public void run() { + if (System.currentTimeMillis() - startTime > 10000) return; + if (c.isShowing()) { + final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); + if (owner != null && owner == c) { + myManager.getFocusManager().requestFocus(new FocusCommand() { + @NotNull + public ActionCallback run() { + return new ActionCallback.Done(); + } + }, myForced).doWhenProcessed(new Runnable() { + public void run() { + updateToolWindow(c); + } + }).notify(result); + } + else { + myManager.getFocusManager().requestFocus(new FocusCommand.ByComponent(c, myToolWindow.getComponent()), myForced) + .doWhenProcessed(new Runnable() { + public void run() { + updateToolWindow(c); + } + }).notify(result); + } } - }, myForced).doWhenProcessed(new Runnable() { - public void run() { - updateToolWindow(c); + else { + checkerAlarm.addRequest(this, 100); } - }).notify(result); - } - else { - myManager.getFocusManager().requestFocus(new FocusCommand.ByComponent(c, myToolWindow.getComponent()), myForced).doWhenProcessed(new Runnable() { - public void run() { - updateToolWindow(c); - } - }).notify(result); - } - + } + }; + checkerAlarm.addRequest(checker, 0); return result; } diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index ac59758878ef..1897cd54dc7a 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -909,7 +909,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { Shape shape; if (balloon.myShowPointer) { - shape = getPointingShape(bounds, g, pointTarget, balloon); + shape = getPointingShape(bounds, pointTarget, balloon); } else { shape = new RoundRectangle2D.Double(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1, balloon.getArc(), balloon.getArc()); @@ -957,7 +957,6 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { protected abstract Insets getTitleInsets(int normalInset, int pointerLength); protected abstract Shape getPointingShape(final Rectangle bounds, - final Graphics2D g, final Point pointTarget, final BalloonImpl balloon); @@ -1060,7 +1059,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { } @Override - protected Shape getPointingShape(final Rectangle bounds, final Graphics2D g, final Point pointTarget, final BalloonImpl balloon) { + protected Shape getPointingShape(final Rectangle bounds, final Point pointTarget, final BalloonImpl balloon) { final Shaper shaper = new Shaper(balloon, bounds, pointTarget, SwingConstants.TOP); shaper.line(balloon.getPointerWidth(this) / 2, balloon.getPointerLength(this)).toRightCurve().roundRightDown().toBottomCurve().roundLeftDown() .toLeftCurve().roundLeftUp().toTopCurve().roundUpRight() @@ -1117,7 +1116,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { } @Override - protected Shape getPointingShape(final Rectangle bounds, final Graphics2D g, final Point pointTarget, final BalloonImpl balloon) { + protected Shape getPointingShape(final Rectangle bounds, final Point pointTarget, final BalloonImpl balloon) { final Shaper shaper = new Shaper(balloon, bounds, pointTarget, SwingConstants.BOTTOM); shaper.line(-balloon.getPointerWidth(this) / 2, -balloon.getPointerLength(this) + 1); shaper.toLeftCurve().roundLeftUp().toTopCurve().roundUpRight().toRightCurve().roundRightDown().toBottomCurve().line(0, 2) @@ -1175,7 +1174,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { } @Override - protected Shape getPointingShape(final Rectangle bounds, final Graphics2D g, final Point pointTarget, final BalloonImpl balloon) { + protected Shape getPointingShape(final Rectangle bounds, final Point pointTarget, final BalloonImpl balloon) { final Shaper shaper = new Shaper(balloon, bounds, pointTarget, SwingConstants.LEFT); shaper.line(balloon.getPointerLength(this), -balloon.getPointerWidth(this) / 2).toTopCurve().roundUpRight().toRightCurve().roundRightDown() .toBottomCurve().roundLeftDown().toLeftCurve().roundLeftUp() @@ -1232,7 +1231,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { } @Override - protected Shape getPointingShape(final Rectangle bounds, final Graphics2D g, final Point pointTarget, final BalloonImpl balloon) { + protected Shape getPointingShape(final Rectangle bounds, final Point pointTarget, final BalloonImpl balloon) { final Shaper shaper = new Shaper(balloon, bounds, pointTarget, SwingConstants.RIGHT); shaper.lineTo((int)bounds.getMaxX() - shaper.getTargetDelta(SwingConstants.RIGHT) - 1, pointTarget.y + balloon.getPointerWidth(this) / 2); shaper.toBottomCurve().roundLeftDown().toLeftCurve().roundLeftUp().toTopCurve().roundUpRight().toRightCurve().roundRightDown() @@ -1393,6 +1392,20 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { } } + @Override + public boolean contains(int x, int y) { + Point pointTarget = SwingUtilities.convertPoint(myLayeredPane, myBalloon.myTargetPoint, this); + Rectangle bounds = myContent.getBounds(); + Shape shape; + if (myShowPointer) { + shape = myBalloon.myPosition.getPointingShape(bounds, pointTarget, myBalloon); + } + else { + shape = new RoundRectangle2D.Double(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1, myBalloon.getArc(), myBalloon.getArc()); + } + return shape.contains(x, y); + } + private void initComponentImage(Point pointTarget, Rectangle shapeBounds) { if (myImage != null) return; @@ -1591,4 +1604,8 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { public boolean isAnimationEnabled() { return myAnimationEnabled; } + + public boolean isBlockClicks() { + return myBlockClicks; + } } diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index a7c02e6cba90..9088b59dd45e 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -338,6 +338,7 @@ checkbox.right.margin=Show right margin (configured in Code Style options) checkbox.use.block.caret=Use block caret checkbox.caret.blinking.ms=Caret blinking (ms): checkbox.mark.modified.tabs.with.asterisk=Mark modified tabs with asterisk +checkbox.show.tabs.tooltips=Show tabs tooltips group.code.folding=Code Folding checkbox.collapse.xml.tags=XML tags checkbox.collapse.html.style.attribute=HTML 'style' attribute diff --git a/platform/platform-resources/src/META-INF/VcsExtensions.xml b/platform/platform-resources/src/META-INF/VcsExtensions.xml index a8ef04f81c62..c792be993dcd 100644 --- a/platform/platform-resources/src/META-INF/VcsExtensions.xml +++ b/platform/platform-resources/src/META-INF/VcsExtensions.xml @@ -15,6 +15,7 @@ + diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index 761be2275b3e..898b55aae3ea 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -334,6 +334,7 @@ + diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java b/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java index d192ef3b5016..865865699645 100644 --- a/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java +++ b/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java @@ -73,7 +73,6 @@ public class FileListeningTest extends IntegrationTestCase { myRoot.refresh(false, true); List changes = getVcs().getChangeListInTests().getChangesInTests().get(0).getChanges(); - assertEquals(changes.toString(), 4, changes.size()); List actual = new SmartList(); for (Change each : changes) { actual.add(((StructuralChange)each).getPath()); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/diff/impl/incrementalMerge/MergeBuilderTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/diff/impl/incrementalMerge/MergeBuilderTest.java index 898f4b8705ba..61efdb64fd18 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/diff/impl/incrementalMerge/MergeBuilderTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/diff/impl/incrementalMerge/MergeBuilderTest.java @@ -29,7 +29,7 @@ public class MergeBuilderTest extends TestCase { addLeft(range, range); addRight(range, range); CHECK.singleElement(finish(1, 1, 2), - fragment(null, new TextRange(1, 1), new TextRange(1, 2))); + fragment(new TextRange(1, 1), new TextRange(1, 1), new TextRange(1, 2))); } public void testSameInsertsConflicts1() { @@ -38,7 +38,7 @@ public class MergeBuilderTest extends TestCase { addLeft(base, version); addRight(base, version); CHECK.singleElement(finish(2, 1, 2), - fragment(new TextRange(0, 1), TextRange.EMPTY_RANGE, new TextRange(0, 1))); + fragment(new TextRange(0, 1), new TextRange(0, 0), new TextRange(0, 1))); } public void testSameInsertsConflicts2() { @@ -52,22 +52,19 @@ public class MergeBuilderTest extends TestCase { }, finish(2, 2, 2)); } - private MergeFragment fragment(TextRange left, TextRange base, TextRange right) { - return new MergeFragment(left, base, right); - } - public void testHeadInsert() { TextRange range = new TextRange(0, 1); addRight(range, new TextRange(1, 2)); addLeft(range, range); CHECK.singleElement(finish(1, 1, 2), - fragment(null, TextRange.EMPTY_RANGE, new TextRange(0, 1))); + fragment(new TextRange(0, 0), new TextRange(0, 0), new TextRange(0, 1))); } public void testOneSideChange() { - addLeft(new TextRange(1, 2), new TextRange(2, 3)); addRight(new TextRange(0, 2), new TextRange(0, 2)); - CHECK.singleElement(finish(3, 2, 2), fragment(new TextRange(0, 2), new TextRange(0, 1), null)); + addLeft(new TextRange(1, 2), new TextRange(2, 3)); + CHECK.singleElement(finish(3, 2, 2), + fragment(new TextRange(0, 2), new TextRange(0, 1), new TextRange(0, 1))); } public void testNotAllignedConflict() { @@ -75,10 +72,18 @@ public class MergeBuilderTest extends TestCase { addRight(new TextRange(2, 4), new TextRange(1, 3)); CHECK.compareAll(new MergeFragment[]{ fragment(new TextRange(0, 1), new TextRange(0, 2), new TextRange(0, 1)), - fragment(new TextRange(2, 3), new TextRange(3, 4), null) + fragment(new TextRange(2, 3), new TextRange(3, 4), new TextRange(2, 3)) }, finish(3, 4, 3)); } + public void testBug() { + addRight(new TextRange(0, 1), new TextRange(0, 1)); + addLeft(new TextRange(0, 2), new TextRange(0, 2)); + CHECK.compareAll(new MergeFragment[]{ + fragment(new TextRange(1, 3), new TextRange(1, 2), new TextRange(1, 1)), + }, finish(3, 2, 1)); + } + public void testMultiChanges() { addLeft(new TextRange(1, 8), new TextRange(1, 8)); addRight(new TextRange(1, 2), new TextRange(0, 1)); @@ -87,10 +92,10 @@ public class MergeBuilderTest extends TestCase { addRight(new TextRange(6, 7), new TextRange(5, 6)); addLeft(new TextRange(9, 10), new TextRange(9, 10)); CHECK.compareAll(new MergeFragment[]{ - fragment(new TextRange(0, 1), new TextRange(0, 1), TextRange.EMPTY_RANGE), - fragment(null, new TextRange(2, 3), new TextRange(1, 1)), - fragment(null, new TextRange(4, 4), new TextRange(2, 3)), - fragment(null, new TextRange(5, 6), new TextRange(4, 5)), + fragment(new TextRange(0, 1), new TextRange(0, 1), new TextRange(0, 0)), + fragment(new TextRange(2, 3), new TextRange(2, 3), new TextRange(1, 1)), + fragment(new TextRange(4, 4), new TextRange(4, 4), new TextRange(2, 3)), + fragment(new TextRange(5, 6), new TextRange(5, 6), new TextRange(4, 5)), fragment(new TextRange(7, 10), new TextRange(7, 10), new TextRange(6, 7)) }, finish(10, 10, 7)); } @@ -105,6 +110,10 @@ public class MergeBuilderTest extends TestCase { }, finish(3, 5, 3)); } + private MergeFragment fragment(TextRange left, TextRange base, TextRange right) { + return new MergeFragment(left, base, right); + } + private void addRight(TextRange base, TextRange right) { myMergeBuilder.add(base, right, FragmentSide.SIDE2); } @@ -121,7 +130,8 @@ public class MergeBuilderTest extends TestCase { protected void runTest() throws Throwable { try { super.runTest(); - } finally { + } + finally { if (IdeaLogger.ourErrorsOccurred != null) throw IdeaLogger.ourErrorsOccurred; } } diff --git a/platform/remote-servers/impl/resources/resources/cloud.properties b/platform/remote-servers/impl/resources/resources/cloud.properties index 24aae89b33a7..3427d98f0d97 100644 --- a/platform/remote-servers/impl/resources/resources/cloud.properties +++ b/platform/remote-servers/impl/resources/resources/cloud.properties @@ -6,3 +6,4 @@ failed.reset.remote=Failed to set {0} repository as remote cloning.existing.application=Cloning existing {0} application fetching.application=Fetching {0} application run.configuration.name={0} - {1} +choose.account.title=Choose {0} account\: diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/VcsNotifier.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/VcsNotifier.java new file mode 100644 index 000000000000..a98734da85b8 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/VcsNotifier.java @@ -0,0 +1,141 @@ +/* + * 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.openapi.vcs; + +import com.intellij.notification.*; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class VcsNotifier { + + private static final NotificationGroup NOTIFICATION_GROUP_ID = NotificationGroup.toolWindowGroup( + "Vcs Messages", ChangesViewContentManager.TOOLWINDOW_ID, true); + private static final NotificationGroup IMPORTANT_ERROR_NOTIFICATION = new NotificationGroup( + "Vcs Important Messages", NotificationDisplayType.STICKY_BALLOON, true); + private static final NotificationGroup MINOR_NOTIFICATION = new NotificationGroup( + "Vcs Minor Notifications", NotificationDisplayType.BALLOON, true); + + private final @NotNull Project myProject; + + + public static VcsNotifier getInstance(@NotNull Project project) { + return ServiceManager.getService(project, VcsNotifier.class); + } + + public VcsNotifier(@NotNull Project project) { + myProject = project; + } + + @NotNull + private static Notification createNotification(@NotNull NotificationGroup notificationGroup, + @NotNull String title, @NotNull String message, @NotNull NotificationType type, + @Nullable NotificationListener listener) { + // title can be empty; message can't be neither null, nor empty + if (StringUtil.isEmptyOrSpaces(message)) { + message = title; + title = ""; + } + // if both title and message were empty, then it is a problem in the calling code => Notifications engine assertion will notify. + return notificationGroup.createNotification(title, message, type, listener); + } + + public void notify(@NotNull Notification notification) { + //todo should be replaced; used only for GithubNotifications + notification.notify(myProject); + } + + @NotNull + protected Notification notify(@NotNull NotificationGroup notificationGroup, @NotNull String title, @NotNull String message, + @NotNull NotificationType type, @Nullable NotificationListener listener) { + Notification notification = createNotification(notificationGroup, title, message, type, listener); + notification.notify(myProject); + return notification; + } + + @NotNull + public Notification notifyError(@NotNull String title, @NotNull String message) { + return notifyError(title, message, null); + } + + @NotNull + public Notification notifyError(@NotNull String title, @NotNull String message, @Nullable NotificationListener listener) { + return notify(IMPORTANT_ERROR_NOTIFICATION, title, message, NotificationType.ERROR, listener); + } + + @NotNull + public Notification notifyWeakError(@NotNull String message) { + return notify(NOTIFICATION_GROUP_ID, "", message, NotificationType.ERROR, null); + } + + @NotNull + public Notification notifySuccess(@NotNull String message) { + return notifySuccess("", message); + } + + @NotNull + public Notification notifySuccess(@NotNull String title, @NotNull String message) { + return notify(NOTIFICATION_GROUP_ID, title, message, NotificationType.INFORMATION, null); + } + + @NotNull + public Notification notifySuccess(@NotNull String title, @NotNull String message, @Nullable NotificationListener listener) { + return notify(NOTIFICATION_GROUP_ID, title, message, NotificationType.INFORMATION, listener); + } + + @NotNull + public Notification notifyImportantInfo(@NotNull String title, @NotNull String message, @Nullable NotificationListener listener) { + return notify(IMPORTANT_ERROR_NOTIFICATION, title, message, NotificationType.INFORMATION, listener); + } + + @NotNull + public Notification notifyInfo(@NotNull String message) { + return notify(NOTIFICATION_GROUP_ID, "", message, NotificationType.INFORMATION, null); + } + + @NotNull + public Notification notifyMinorWarning(@NotNull String title, @NotNull String message, @Nullable NotificationListener listener) { + return notify(MINOR_NOTIFICATION, title, message, NotificationType.WARNING, listener); + } + + @NotNull + public Notification notifyMinorWarning(@NotNull String title, @NotNull String message) { + return notifyMinorWarning(title, message, null); + } + + @NotNull + public Notification notifyWarning(@NotNull String title, @NotNull String message) { + return notify(NOTIFICATION_GROUP_ID, title, message, NotificationType.WARNING, null); + } + + @NotNull + public Notification notifyImportantWarning(@NotNull String title, @NotNull String message, @Nullable NotificationListener listener) { + return notify(IMPORTANT_ERROR_NOTIFICATION, title, message, NotificationType.WARNING, listener); + } + + @NotNull + public Notification notifyMinorInfo(@NotNull String title, @NotNull String message) { + return notifyMinorInfo(title, message, null); + } + + @NotNull + public Notification notifyMinorInfo(@NotNull String title, @NotNull String message, @Nullable NotificationListener listener) { + return notify(MINOR_NOTIFICATION, title, message, NotificationType.INFORMATION, listener); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/roots/VcsIntegrationEnabler.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/roots/VcsIntegrationEnabler.java new file mode 100644 index 000000000000..e395651eee20 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/roots/VcsIntegrationEnabler.java @@ -0,0 +1,112 @@ +/* + * 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.openapi.vcs.roots; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +import static com.intellij.openapi.util.text.StringUtil.pluralize; + +public abstract class VcsIntegrationEnabler> { + + protected final @NotNull Project myProject; + protected final @NotNull VcsT myVcs; + + + protected VcsIntegrationEnabler(@NotNull VcsT vcs) { + myProject = vcs.getProject(); + myVcs = vcs; + } + + public void enable(@NotNull Collection vcsRoots) { + Collection vcsFilterRoots = ContainerUtil.filter(vcsRoots, new Condition() { + @Override + public boolean value(VcsRoot root) { + AbstractVcs vcs = root.getVcs(); + return vcs != null && vcs.getName().equals(myVcs.getName()); + } + }); + Collection roots = VcsRootErrorsFinder.vcsRootsToVirtualFiles(vcsFilterRoots); + VirtualFile projectDir = myProject.getBaseDir(); + assert projectDir != null : "Base dir is unexpectedly null for project: " + myProject; + + if (vcsFilterRoots.isEmpty()) { + boolean succeeded = initOrNotifyError(projectDir); + if (succeeded) { + addVcsRoots(Collections.singleton(projectDir)); + } + } + else { + assert !roots.isEmpty(); + if (roots.size() > 1 || isProjectBelowVcs(roots)) { + notifyAddedRoots(roots); + } + addVcsRoots(roots); + } + } + + private boolean isProjectBelowVcs(@NotNull Collection vcsRoots) { + //check if there are vcs roots strictly above the project dir + VirtualFile baseDir = myProject.getBaseDir(); + for (VirtualFile root : vcsRoots) { + if (VfsUtilCore.isAncestor(root, baseDir, true)) { + return true; + } + } + return false; + } + + @NotNull + public static String joinRootsPaths(@NotNull Collection roots) { + return StringUtil.join(roots, new Function() { + @Override + public String fun(VirtualFile virtualFile) { + return virtualFile.getPresentableUrl(); + } + }, ", "); + } + + protected abstract boolean initOrNotifyError(@NotNull final VirtualFile projectDir); + + protected void notifyAddedRoots(Collection roots) { + VcsNotifier notifier = VcsNotifier.getInstance(myProject); + notifier + .notifySuccess("", String.format("Added %s %s: %s", myVcs.getName(), pluralize("root", roots.size()), joinRootsPaths(roots))); + } + + private void addVcsRoots(@NotNull Collection roots) { + ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject); + List currentVcsRoots = Arrays.asList(vcsManager.getRootsUnderVcs(myVcs)); + + List mappings = new ArrayList(vcsManager.getDirectoryMappings(myVcs)); + + for (VirtualFile root : roots) { + if (!currentVcsRoots.contains(root)) { + mappings.add(new VcsDirectoryMapping(root.getPath(), myVcs.getName())); + } + } + vcsManager.setDirectoryMappings(mappings); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/roots/VcsRootProblemNotifier.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/roots/VcsRootProblemNotifier.java index c8b89a09ebf3..fcafc513fcb8 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/roots/VcsRootProblemNotifier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/roots/VcsRootProblemNotifier.java @@ -16,7 +16,8 @@ package com.intellij.openapi.vcs.roots; import com.intellij.idea.ActionsBundle; -import com.intellij.notification.*; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationListener; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; @@ -24,6 +25,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsConfiguration; import com.intellij.openapi.vcs.VcsDirectoryMapping; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.VcsRootError; import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; @@ -33,8 +35,6 @@ import javax.swing.event.HyperlinkEvent; import java.util.ArrayList; import java.util.Collection; -import static com.intellij.notification.NotificationType.ERROR; -import static com.intellij.notification.NotificationType.INFORMATION; import static com.intellij.openapi.util.text.StringUtil.pluralize; /** @@ -50,11 +50,6 @@ public class VcsRootProblemNotifier { private @Nullable Notification myNotification; private final @NotNull Object NOTIFICATION_LOCK = new Object(); - public static final NotificationGroup IMPORTANT_ERROR_NOTIFICATION = new NotificationGroup( - "Vcs Important Messages", NotificationDisplayType.STICKY_BALLOON, true); - public static final NotificationGroup MINOR_NOTIFICATION = new NotificationGroup( - "Vcs Minor Notifications", NotificationDisplayType.BALLOON, true); - public static VcsRootProblemNotifier getInstance(@NotNull Project project) { return new VcsRootProblemNotifier(project); } @@ -85,11 +80,11 @@ public class VcsRootProblemNotifier { synchronized (NOTIFICATION_LOCK) { expireNotification(); - NotificationGroup notificationGroup = invalidRoots.isEmpty() ? MINOR_NOTIFICATION : IMPORTANT_ERROR_NOTIFICATION; - NotificationType notificationType = invalidRoots.isEmpty() ? INFORMATION : ERROR; - myNotification = notificationGroup.createNotification(title, description, notificationType, - new MyNotificationListener(myProject, mySettings)); - myNotification.notify(myProject); + NotificationListener listener = new MyNotificationListener(myProject, mySettings); + VcsNotifier notifier = VcsNotifier.getInstance(myProject); + myNotification = invalidRoots.isEmpty() + ? notifier.notifyMinorInfo(title, description, listener) + : notifier.notifyError(title, description, listener); } } diff --git a/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/TestVcsNotifier.java b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/TestVcsNotifier.java new file mode 100644 index 000000000000..fabf2f2e9b44 --- /dev/null +++ b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/TestVcsNotifier.java @@ -0,0 +1,50 @@ +/* + * 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.openapi.vcs; + +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationGroup; +import com.intellij.notification.NotificationListener; +import com.intellij.notification.NotificationType; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class TestVcsNotifier extends VcsNotifier { + + public static final String TEST_NOTIFICATION_GROUP = "Test"; + + private Notification myLastNotification; + + public TestVcsNotifier(@NotNull Project project) { + super(project); + } + + public Notification getLastNotification() { + return myLastNotification; + } + + @NotNull + public Notification notify(@NotNull NotificationGroup notificationGroup, @NotNull String title, @NotNull String message, + @NotNull NotificationType type, @Nullable NotificationListener listener) { + myLastNotification = new Notification(TEST_NOTIFICATION_GROUP, title, message, type, listener); + return myLastNotification; + } + + public void cleanup() { + myLastNotification = null; + } +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogBranchFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogBranchFilter.java index 1c0ddec718d8..dfa78da2fab3 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogBranchFilter.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogBranchFilter.java @@ -22,9 +22,12 @@ import java.util.Collection; /** * Tells to filter by some branch name. */ -public interface VcsLogBranchFilter extends VcsLogGraphFilter { +public interface VcsLogBranchFilter extends VcsLogFilter { @NotNull Collection getBranchNames(); + @NotNull + Collection getMatchingHeads(); + } diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogFilterCollection.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogFilterCollection.java index a1c10ec313eb..19cfbdf486eb 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogFilterCollection.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogFilterCollection.java @@ -47,9 +47,6 @@ public interface VcsLogFilterCollection { */ boolean isEmpty(); - @NotNull - List getGraphFilters(); - @NotNull List getDetailsFilters(); diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogGraphFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogGraphFilter.java deleted file mode 100644 index 4475b5da40c6..000000000000 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogGraphFilter.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.intellij.vcs.log; - -/** - * Filter that is able to work on graph. - */ -public interface VcsLogGraphFilter extends VcsLogFilter { - - boolean matches(int hash); - -} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java index a325ece99c03..467368e80b19 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java @@ -74,14 +74,13 @@ public interface VcsLogProvider { void subscribeToRootRefreshEvents(@NotNull Collection roots, @NotNull VcsLogRefresher refresher); /** - *

Return commits with full details, which correspond to the given filters.

+ *

Return commits, which correspond to the given filters.

* * @param maxCount maximum number of commits to request from the VCS, or -1 for unlimited. */ @NotNull - List getFilteredDetails(@NotNull VirtualFile root, - @NotNull VcsLogFilterCollection filterCollection, - int maxCount) throws VcsException; + List getCommitsMatchingFilter(@NotNull VirtualFile root, @NotNull VcsLogFilterCollection filterCollection, int maxCount) + throws VcsException; /** * Returns the name of current user as specified for the given root, diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphAction.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphAction.java new file mode 100644 index 000000000000..a08aaaf928c7 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphAction.java @@ -0,0 +1,19 @@ +/* + * 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.vcs.log.graph; + +public interface GraphAction { +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphActionRequest.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphActionRequest.java new file mode 100644 index 000000000000..a963da2a5ae5 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphActionRequest.java @@ -0,0 +1,24 @@ +/* + * 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.vcs.log.graph; + +/** + * Action which should be executed by the code which called {@link GraphFacade#performAction(com.intellij.vcs.log.graph.GraphAction)}. + * E.g. when user clicks to the arrow to go to the parent/child commit, the table should jump to concrete row. + */ +public interface GraphActionRequest { + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphAnswer.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphAnswer.java new file mode 100644 index 000000000000..57c985155e89 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphAnswer.java @@ -0,0 +1,40 @@ +/* + * 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.vcs.log.graph; + +import org.jetbrains.annotations.Nullable; + +/** + * The graph returns an instance of the GraphAnswer as a reaction to {@link #performAction(com.intellij.vcs.log.graph.GraphAction)}. + */ +public interface GraphAnswer { + + /** + * Tells how graph was changed after performing action
+ * {@code null} means that graph didn't change (e.g. when user clicks on the arrow to go to the parent/child commit). + */ + @Nullable + GraphChange getGraphChange(); + + /** + * Tells which action should be execute by the client code.
+ * {@code null} means that the graph doesn't expect the client code to execute anything special + * (although of course it can react somehow on the returned {@link #getGraphChange() GraphChange}. + */ + @Nullable + GraphActionRequest getActionRequest(); + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphChange.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphChange.java new file mode 100644 index 000000000000..41cdaa1f14a6 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphChange.java @@ -0,0 +1,24 @@ +/* + * 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.vcs.log.graph; + +/** + * Describes how graph was changed after {@link GraphFacade#performAction(GraphAction) executing some action on it} + * (e.g. a linear branch was collapsed). + */ +public interface GraphChange { + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphColorManager.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphColorManager.java new file mode 100644 index 000000000000..caa82f2b93aa --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphColorManager.java @@ -0,0 +1,52 @@ +/* + * 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.vcs.log.graph; + +import com.intellij.ui.JBColor; +import org.jetbrains.annotations.NotNull; + +public interface GraphColorManager { + + /** + * Returns the color which should be used to draw the given branch. + * @param headCommit branch head commit index. + */ + @NotNull + JBColor getColorOfBranch(int headCommit); + + /** + * Returns the color which should be used to draw a not-main fragment of a branch + * (e.g. there was master, then I've checked out a feature, made some commits and merged back to master - these my commits form such + * a fragment which is the same branch in terms of VCS, but is a separate branch in terms of graph, + * and therefore we may want to color it separately. + * + * @param headCommit commit of the branch head which the fragment belongs to. + * @param magicIndex some magic index identifying the fragment (we don't know which one - it is some Graph internal thing). + * @return the color which should be used to draw this fragment. + */ + @NotNull + JBColor getColorOfFragment(int headCommit, int magicIndex); + + /** + * Returns 1, 0 or -1 if branch identified by commit {@code head1} is "more powerful", "equally powerful" or "less powerful" + * than the branch identified by commit {@code head2}. + *

+ * If branch1 is more powerful than branch2, it means that its color will be reused by the subgraph below the point when these branches + * were diverged. + */ + int compareHeads(int head1, int head2); + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphFacade.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphFacade.java new file mode 100644 index 000000000000..fa0f3c4c0402 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphFacade.java @@ -0,0 +1,87 @@ +/* + * 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.vcs.log.graph; + +import com.intellij.openapi.util.Condition; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; + +/** + * The only point of interaction with the Graph. + *

+ * Any access to the methods of this class must be performed from the EDT. + */ +public interface GraphFacade { + + /** + * Paints the given row. + */ + @NotNull + PaintInfo paint(int visibleRow); + + /** + * Performs some user action on the graph, which can be a click, hover, drag, changing view parameters, etc.
+ * Changes are applied to the graph immediately, and the method returns the result of such action.
+ * In some cases the action might take significant time (say, 1 second) - clients of this method take care of it themselves: by showing + * some modal progress or else. + */ + @Nullable + GraphAnswer performAction(@NotNull GraphAction action); + + /** + * Returns all commits in the graph without considering commits visibility. + * To get visible commits use {@link #getVisibleCommitCount} & {@link #getCommitAtRow}. + */ + @NotNull + List getAllCommits(); + + /** + * A shorthand to getVisibleCommits().get(visibleRow), but may be faster. + */ + int getCommitAtRow(int visibleRow); + + /** + * A shorthand to {@code getVisibleCommits().size()}, but is faster. + */ + int getVisibleCommitCount(); + + /** + * Set branches which should be visible in the log, all others will be hidden. + * Pass {@code null} to show all branches, i.e. reset this branch filter. + * + * @param heads branches represented by commit indices of commits they point to; or {@code null} if all branches should be shown. + * @see #setFilter(Condition) + */ + void setVisibleBranches(@Nullable Collection heads); + + /** + * Set filter to the commits displayed by the log. + * + * @param visibilityPredicate check if the given commit should be shown or not. null means no filter. + * @see #setVisibleBranches(Collection) + */ + void setFilter(@Nullable Condition visibilityPredicate); + + /** + * Returns the provider of some information about the graph. + */ + @NotNull + GraphInfoProvider getInfoProvider(); + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphInfoProvider.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphInfoProvider.java new file mode 100644 index 000000000000..308a4c6ec0c8 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/GraphInfoProvider.java @@ -0,0 +1,43 @@ +/* + * 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.vcs.log.graph; + +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +/** + * Use this provider to get information from the Graph.
+ * An instance of GraphInfoProvider is obtained via {@link GraphFacade#getInfoProvider()}. + */ +public interface GraphInfoProvider { + + @NotNull + Set getContainingBranches(int visibleRow); // this requires graph iteration => can take some time + + @NotNull + RowInfo getRowInfo(int visibleRow); + + boolean areLongEdgesHidden(); + + /** + * Some information about row highlighting etc. TBD + */ + interface RowInfo { + int getOneOfHeads(); + } + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/JumpToNotLoadCommitActionRequest.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/JumpToNotLoadCommitActionRequest.java new file mode 100644 index 000000000000..c4348f3e6b1c --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/JumpToNotLoadCommitActionRequest.java @@ -0,0 +1,29 @@ +/* + * 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.vcs.log.graph; + +public class JumpToNotLoadCommitActionRequest implements GraphActionRequest { + private final int commitHashIndex; + + public JumpToNotLoadCommitActionRequest(int commitHashIndex) { + this.commitHashIndex = commitHashIndex; + } + + public int getCommitHashIndex() { + return commitHashIndex; + } +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/JumpToRowActionRequest.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/JumpToRowActionRequest.java new file mode 100644 index 000000000000..5b2518b26f8a --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/JumpToRowActionRequest.java @@ -0,0 +1,30 @@ +/* + * 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.vcs.log.graph; + +public class JumpToRowActionRequest implements GraphActionRequest { + + private final int myRow; + + public JumpToRowActionRequest(int row) { + myRow = row; + } + + public int getRow() { + return myRow; + } + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/LinearBranchesExpansionAction.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/LinearBranchesExpansionAction.java new file mode 100644 index 000000000000..dbfd046cf0b6 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/LinearBranchesExpansionAction.java @@ -0,0 +1,32 @@ +/* + * 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.vcs.log.graph; + +public class LinearBranchesExpansionAction implements GraphAction { + + public static LinearBranchesExpansionAction EXPAND = new LinearBranchesExpansionAction(true); + public static LinearBranchesExpansionAction COLLAPSE = new LinearBranchesExpansionAction(false); + + private final boolean myExpand; + + private LinearBranchesExpansionAction(boolean expand) { + myExpand = expand; + } + + public boolean shouldExpand() { + return myExpand; + } +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/LongEdgesAction.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/LongEdgesAction.java new file mode 100644 index 000000000000..4824f0b13c65 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/LongEdgesAction.java @@ -0,0 +1,37 @@ +/* + * 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.vcs.log.graph; + +public class LongEdgesAction implements GraphAction { + + private static LongEdgesAction SHOW = new LongEdgesAction(true); + private static LongEdgesAction HIDE = new LongEdgesAction(false); + + private final boolean myShowLongEdges; + + public static LongEdgesAction valueOf(boolean showLongEdges) { + return showLongEdges ? SHOW : HIDE; + } + + private LongEdgesAction(boolean show) { + myShowLongEdges = show; + } + + public boolean shouldShowLongEdges() { + return myShowLongEdges; + } + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/PaintInfo.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/PaintInfo.java new file mode 100644 index 000000000000..42e56042756e --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/PaintInfo.java @@ -0,0 +1,37 @@ +/* + * 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.vcs.log.graph; + +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +public interface PaintInfo { + + /** + * Returns the image to actually paint. + */ + @NotNull + Image getImage(); + + /** + * Returns the "interesting" width of the painted image, i.e. the width which the text in the table should be offset by.
+ * It can be smaller than the width of {@link #getImage() the image}, because we allow the text to cover part of the graph + * (some diagonal edges, etc.) + */ + int getWidth(); + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/graph/SelectAllRelativeCommitsAction.java b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/SelectAllRelativeCommitsAction.java new file mode 100644 index 000000000000..f35d226692c4 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/graph/SelectAllRelativeCommitsAction.java @@ -0,0 +1,31 @@ +/* + * 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.vcs.log.graph; + +public class SelectAllRelativeCommitsAction implements GraphAction { + public static final SelectAllRelativeCommitsAction DESELECT_ALL = new SelectAllRelativeCommitsAction(-1); + + private final int visibleRowIndex; + + public SelectAllRelativeCommitsAction(int visibleRowIndex) { + this.visibleRowIndex = visibleRowIndex; + } + + public int getVisibleRowIndex() { + return visibleRowIndex; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/ChangeCursorActionRequest.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/ChangeCursorActionRequest.java new file mode 100644 index 000000000000..ec7908295875 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/ChangeCursorActionRequest.java @@ -0,0 +1,33 @@ +/* + * 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.vcs.log.graph; + +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +public class ChangeCursorActionRequest implements GraphActionRequest { + @NotNull private final Cursor myCursor; + + public ChangeCursorActionRequest(@NotNull Cursor cursor) { + myCursor = cursor; + } + + @NotNull + public Cursor getCursor() { + return myCursor; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrDefaultAnnotationValue.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/ClickGraphAction.java similarity index 54% rename from plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrDefaultAnnotationValue.java rename to platform/vcs-log/graph/src/com/intellij/vcs/log/graph/ClickGraphAction.java index 84bd256cc2d0..fc037f701855 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrDefaultAnnotationValue.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/ClickGraphAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -13,18 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.plugins.groovy.lang.psi.api.statements; +package com.intellij.vcs.log.graph; -import com.intellij.psi.PsiAnnotationMemberValue; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationMemberValue; -/** - * User: Dmitry.Krasilschikov - * Date: 04.06.2007 - */ -public interface GrDefaultAnnotationValue extends GroovyPsiElement, PsiAnnotationMemberValue { +import java.awt.*; + +public class ClickGraphAction implements GraphAction { + + private final int myVisibleRow; + @Nullable private final Point myRelativePoint; + + public ClickGraphAction(int visibleRow, @Nullable Point relativePoint) { + myVisibleRow = visibleRow; + myRelativePoint = relativePoint; + } + + public int getRow() { + return myVisibleRow; + } + @Nullable - GrAnnotationMemberValue getDefaultValue(); + public Point getRelativePoint() { + return myRelativePoint; + } + } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/GraphFacadeImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/GraphFacadeImpl.java new file mode 100644 index 000000000000..52c2f9914cf4 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/GraphFacadeImpl.java @@ -0,0 +1,380 @@ +/* + * 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.vcs.log.graph; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Condition; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; +import com.intellij.vcs.log.compressedlist.UpdateRequest; +import com.intellij.vcs.log.graph.elements.Edge; +import com.intellij.vcs.log.graph.elements.GraphElement; +import com.intellij.vcs.log.graph.elements.Node; +import com.intellij.vcs.log.graph.elements.NodeRow; +import com.intellij.vcs.log.graph.render.SimpleGraphCellPainter; +import com.intellij.vcs.log.graphmodel.FragmentManager; +import com.intellij.vcs.log.graphmodel.GraphFragment; +import com.intellij.vcs.log.graphmodel.GraphModel; +import com.intellij.vcs.log.printmodel.GraphPrintCell; +import com.intellij.vcs.log.printmodel.GraphPrintCellModel; +import com.intellij.vcs.log.printmodel.SelectController; +import com.intellij.vcs.log.printmodel.SpecialPrintElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static com.intellij.vcs.log.graph.render.PrintParameters.HEIGHT_CELL; +import static com.intellij.vcs.log.graph.render.PrintParameters.WIDTH_NODE; + +public class GraphFacadeImpl implements GraphFacade { + + private static final Logger LOG = Logger.getInstance(GraphFacadeImpl.class); + private static final Function ALL_NODES_VISIBLE = new Function() { + @Override + public Boolean fun(Node node) { + return true; + } + }; + + // In case of diagonal edges, one node can be at most 3 "arrows" + 2 nodes at the left from another - that is enough for sure + private static final int IMAGE_WIDTH_RESERVE = 5 * WIDTH_NODE; + + @NotNull private final GraphModel myGraphModel; + @NotNull private final GraphPrintCellModel myPrintCellModel; + @NotNull private final GraphColorManager myColorManager; + @NotNull private final SimpleGraphCellPainter myGraphPainter; + + @Nullable private GraphElement prevGraphElement; + + public GraphFacadeImpl(@NotNull GraphModel graphModel, @NotNull GraphPrintCellModel printCellModel, + @NotNull GraphColorManager colorManager) { + myGraphModel = graphModel; + myPrintCellModel = printCellModel; + myColorManager = colorManager; + myGraphPainter = new SimpleGraphCellPainter(); + } + + @NotNull + @Override + public PaintInfo paint(int visibleRow) { + GraphPrintCell cell = myPrintCellModel.getGraphPrintCell(visibleRow); + int imageWidth = calcImageWidth(cell); + int bufferWidth = imageWidth + IMAGE_WIDTH_RESERVE; + BufferedImage image = UIUtil.createImage(bufferWidth, HEIGHT_CELL, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = image.createGraphics(); + myGraphPainter.draw(g2, cell); + return new PaintInfoImpl(image, imageWidth); + } + + private static int calcImageWidth(@NotNull GraphPrintCell cell) { + return cell.countCell() * WIDTH_NODE; + } + + @Nullable + @Override + public GraphAnswer performAction(@NotNull GraphAction action) { + if (action instanceof LinearBranchesExpansionAction) { + FragmentManager fragmentManager = myGraphModel.getFragmentManager(); + if (((LinearBranchesExpansionAction)action).shouldExpand()) { + fragmentManager.showAll(); + } + else { + fragmentManager.hideAll(); + } + } + else if (action instanceof LongEdgesAction) { + myPrintCellModel.setLongEdgeVisibility(((LongEdgesAction)action).shouldShowLongEdges()); + } + else if (action instanceof ClickGraphAction) { + return handleClick((ClickGraphAction)action); + } + else if (action instanceof MouseOverAction) { + return handleMouseOver((MouseOverAction)action); + } + return null; + } + + @Nullable + private GraphAnswer handleMouseOver(MouseOverAction action) { + GraphPrintCell printCell = myPrintCellModel.getGraphPrintCell(action.getRow()); + Node jumpToNode = arrowToNode(action.getRelativePoint(), printCell); + if (jumpToNode != null) { + over(null); + return new ChangeCursorAnswer(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + else { + over(overCell(action.getRelativePoint(), printCell)); + return new ChangeCursorAnswer(Cursor.getDefaultCursor()); + } + } + + @Nullable + private GraphAnswer handleClick(@NotNull ClickGraphAction action) { + if (action.getRelativePoint() == null) { + return handleRowSelection(action.getRow()); + } + else { + GraphPrintCell printCell = myPrintCellModel.getGraphPrintCell(action.getRow()); + return handleMouseClick(action.getRow(), action.getRelativePoint(), printCell); + } + } + + @Nullable + private GraphAnswer handleRowSelection(int row) { + myPrintCellModel.getCommitSelectController().deselectAll(); + Node node = myGraphModel.getGraph().getCommitNodeInRow(row); + if (node != null) { + FragmentManager fragmentController = myGraphModel.getFragmentManager(); + myPrintCellModel.getCommitSelectController().select(fragmentController.allCommitsCurrentBranch(node)); + } + return null; + } + + @Nullable + private GraphAnswer handleMouseClick(int row, @NotNull Point point, @Nullable GraphPrintCell printCell) { + Node jumpToNode = arrowToNode(point, printCell); + if (jumpToNode != null) { + return new JumpToRowAnswer(jumpToNode.getRowIndex()); + } + GraphElement graphElement = overCell(point, printCell); + myPrintCellModel.getSelectController().deselectAll(); + if (graphElement == null) { + return handleRowSelection(row); + } + else { + return click(graphElement); + } + } + + @Nullable + private Node arrowToNode(@NotNull Point point, @Nullable GraphPrintCell row) { + if (row == null) { + return null; + } + SpecialPrintElement printElement = myGraphPainter.mouseOverArrow(row, point.x, point.y); + if (printElement == null) { + return null; + } + Edge edge = printElement.getGraphElement().getEdge(); + if (edge == null) { + return null; + } + return printElement.getType() == SpecialPrintElement.Type.DOWN_ARROW ? edge.getDownNode() : edge.getUpNode(); + } + + @Nullable + public GraphAnswer click(@Nullable GraphElement graphElement) { + FragmentManager fragmentController = myGraphModel.getFragmentManager(); + if (graphElement == null) { + return null; + } + final GraphFragment fragment = fragmentController.relateFragment(graphElement); + if (fragment == null) { + return null; + } + + UpdateRequest updateRequest = fragmentController.changeVisibility(fragment); + return new JumpToRowAnswer(updateRequest.from()); + } + + public void over(@Nullable GraphElement graphElement) { + SelectController selectController = myPrintCellModel.getSelectController(); + FragmentManager fragmentManager = myGraphModel.getFragmentManager(); + if (graphElement == prevGraphElement) { + return; + } + else { + prevGraphElement = graphElement; + } + selectController.deselectAll(); + if (graphElement != null) { + GraphFragment graphFragment = fragmentManager.relateFragment(graphElement); + selectController.select(graphFragment); + } + } + + @NotNull + @Override + public List getAllCommits() { + throw new UnsupportedOperationException(); + } + + @Nullable + private GraphElement overCell(@NotNull Point point, @Nullable GraphPrintCell row) { + int y = point.y; + int x = point.x; + return row != null ? myGraphPainter.mouseOver(row, x, y) : null; + } + + @Override + public int getCommitAtRow(int visibleRow) { + NodeRow row = myGraphModel.getGraph().getNodeRows().get(visibleRow); + return getCommit(row); + } + + private static int getCommit(@NotNull NodeRow nodeRow) { + List nodes = nodeRow.getNodes(); + if (nodes.size() < 1) { + LOG.error("No nodes for nodeRow: " + nodeRow); + return -1; + } + else { + if (nodes.size() > 1 && existsNotEndNode(nodes)) { // allowed for END_NODES, i.e. at the bottom of the partly loaded log + LOG.error("Too many nodes for nodeRow: " + nodeRow); + } + return nodes.get(0).getCommitIndex(); + } + } + + @Override + public int getVisibleCommitCount() { + return myGraphModel.getGraph().getNodeRows().size(); + } + + private static boolean existsNotEndNode(@NotNull List nodes) { + return ContainerUtil.exists(nodes, new Condition() { + @Override + public boolean value(Node node) { + return node.getType() != Node.NodeType.END_COMMIT_NODE; + } + }); + } + + @Override + public void setVisibleBranches(@Nullable final Collection heads) { + myGraphModel.setVisibleBranchesNodes(heads == null ? ALL_NODES_VISIBLE : new Function() { + @Override + public Boolean fun(final Node node) { + return heads.contains(node.getCommitIndex()); + } + }); + } + + @Override + public void setFilter(@NotNull Condition visibilityPredicate) { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public GraphInfoProvider getInfoProvider() { + return new GraphInfoProviderImpl(); + } + + private class GraphInfoProviderImpl implements GraphInfoProvider { + + @NotNull + @Override + public Set getContainingBranches(int visibleRow) { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public RowInfo getRowInfo(int visibleRow) { + return new RowInfoImpl(visibleRow); + } + + @Override + public boolean areLongEdgesHidden() { + return myPrintCellModel.areLongEdgesHidden(); + } + } + + private class RowInfoImpl implements GraphInfoProvider.RowInfo { + private final int myVisibleRow; + + public RowInfoImpl(int visibleRow) { + myVisibleRow = visibleRow; + } + + @Override + public int getOneOfHeads() { + Node node = myGraphModel.getGraph().getCommitNodeInRow(myVisibleRow); + assert node != null : "node is null for row " + myVisibleRow; + return node.getBranch().getOneOfHeads(); + } + } + + private static class JumpToRowAnswer implements GraphAnswer { + private final int myRow; + + public JumpToRowAnswer(int row) { + myRow = row; + } + + @Nullable + @Override + public GraphChange getGraphChange() { + return null; + } + + @Nullable + @Override + public GraphActionRequest getActionRequest() { + return new JumpToRowActionRequest(myRow); + } + + } + + private static class ChangeCursorAnswer implements GraphAnswer { + private final Cursor myCursor; + + public ChangeCursorAnswer(@NotNull Cursor cursor) { + myCursor = cursor; + } + + @Nullable + @Override + public GraphChange getGraphChange() { + return null; + } + + @Nullable + @Override + public GraphActionRequest getActionRequest() { + return new ChangeCursorActionRequest(myCursor); + } + } + + private static class PaintInfoImpl implements PaintInfo { + + private final Image myImage; + private final int myWidth; + + public PaintInfoImpl(Image image, int width) { + myImage = image; + myWidth = width; + } + + @NotNull + @Override + public Image getImage() { + return myImage; + } + + @Override + public int getWidth() { + return myWidth; + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/MouseOverAction.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/MouseOverAction.java new file mode 100644 index 000000000000..ca619c6e55f3 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/MouseOverAction.java @@ -0,0 +1,41 @@ +/* + * 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.vcs.log.graph; + +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +public class MouseOverAction implements GraphAction { + + private final int myVisibleRow; + @NotNull private final Point myRelativePoint; + + public MouseOverAction(int visibleRow, @NotNull Point relativePoint) { + myVisibleRow = visibleRow; + myRelativePoint = relativePoint; + } + + public int getRow() { + return myVisibleRow; + } + + @NotNull + public Point getRelativePoint() { + return myRelativePoint; + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/elements/Branch.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/elements/Branch.java index de249613a219..86ae08b9d66e 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/elements/Branch.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/elements/Branch.java @@ -1,7 +1,6 @@ package com.intellij.vcs.log.graph.elements; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.VcsRef; import org.jetbrains.annotations.NotNull; @@ -16,13 +15,12 @@ public final class Branch { private final int upCommitHash; private final int downCommitHash; @Nullable private final VcsRef myColoredRef; - @NotNull private final VirtualFile myRepositoryRoot; + private final int myOneOfHeads; - public Branch(int upCommitHash, int downCommitHash, @NotNull Collection refs, - @NotNull VirtualFile repositoryRoot) { + public Branch(int upCommitHash, int downCommitHash, @NotNull Collection refs, int oneOfHeads) { this.upCommitHash = upCommitHash; this.downCommitHash = downCommitHash; - myRepositoryRoot = repositoryRoot; + myOneOfHeads = oneOfHeads; myColoredRef = findRefForBranchColor(refs); } @@ -36,8 +34,8 @@ public final class Branch { }); } - public Branch(int commit, @NotNull Collection refs, @NotNull VirtualFile repositoryRoot) { - this(commit, commit, refs, repositoryRoot); + public Branch(int commit, @NotNull Collection refs, int oneOfHeads) { + this(commit, commit, refs, oneOfHeads); } public int getUpCommitHash() { @@ -79,9 +77,8 @@ public final class Branch { } } - @NotNull - public VirtualFile getRepositoryRoot() { - return myRepositoryRoot; + public int getOneOfHeads() { + return myOneOfHeads; } } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/mutable/GraphBuilder.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/mutable/GraphBuilder.java index b07d876a1e9a..65b8ec6e36b1 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/mutable/GraphBuilder.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/mutable/GraphBuilder.java @@ -1,8 +1,6 @@ package com.intellij.vcs.log.graph.mutable; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.newvfs.impl.NullVirtualFile; import com.intellij.util.containers.MultiMap; import com.intellij.vcs.log.GraphCommit; import com.intellij.vcs.log.VcsRef; @@ -14,7 +12,8 @@ import org.jetbrains.annotations.NotNull; import java.util.*; -import static com.intellij.vcs.log.graph.elements.Node.NodeType.*; +import static com.intellij.vcs.log.graph.elements.Node.NodeType.COMMIT_NODE; +import static com.intellij.vcs.log.graph.elements.Node.NodeType.END_COMMIT_NODE; /** * @author erokhins @@ -90,16 +89,16 @@ public class GraphBuilder { @NotNull protected Branch createBranch(int commitHash, @NotNull Collection refs) { - VirtualFile repositoryRoot; + int oneOfHeads; if (refs.isEmpty()) { // should never happen, but fallback gently. LOG.error("Ref should exist for this node. Hash: " + commitHash); - repositoryRoot = NullVirtualFile.INSTANCE; + oneOfHeads = -1; } else { - repositoryRoot = refs.iterator().next().getRoot(); + oneOfHeads = refs.iterator().next().getCommitIndex(); } - return new Branch(commitHash, refs, repositoryRoot); + return new Branch(commitHash, refs, oneOfHeads); } private void addParent(MutableNode node, int parentHash, Branch branch) { @@ -126,7 +125,7 @@ public class GraphBuilder { else { for (int parentHash : parents) { Collection refs = findRefForHash(node.getCommitIndex()); - addParent(node, parentHash, new Branch(node.getCommitIndex(), parentHash, refs, branch.getRepositoryRoot())); + addParent(node, parentHash, new Branch(node.getCommitIndex(), parentHash, refs, branch.getOneOfHeads())); } } } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/ColorGenerator.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/ColorGenerator.java index 8471d005d070..d87d920c6820 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/ColorGenerator.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/ColorGenerator.java @@ -1,7 +1,6 @@ package com.intellij.vcs.log.graph.render; import com.intellij.util.containers.ContainerUtil; -import com.intellij.vcs.log.graph.elements.Branch; import org.jetbrains.annotations.NotNull; import java.awt.*; @@ -15,12 +14,11 @@ public class ColorGenerator { private static final Map ourColorMap = ContainerUtil.newHashMap(); @NotNull - public static Color getColor(@NotNull Branch branch) { - int indexColor = branch.getBranchNumber(); - Color color = ourColorMap.get(indexColor); + public static Color getColor(int branchNumber) { + Color color = ourColorMap.get(branchNumber); if (color == null) { - color = getColor(indexColor); - ourColorMap.put(indexColor, color); + color = calcColor(branchNumber); + ourColorMap.put(branchNumber, color); } return color; } @@ -30,7 +28,7 @@ public class ColorGenerator { } @NotNull - private static Color getColor(int indexColor) { + private static Color calcColor(int indexColor) { int r = indexColor * 200 + 30; int g = indexColor * 130 + 50; int b = indexColor * 90 + 100; diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/GraphCellPainter.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/GraphCellPainter.java index b2fe9d65e966..8c1e32e3e645 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/GraphCellPainter.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/GraphCellPainter.java @@ -6,7 +6,6 @@ import com.intellij.vcs.log.printmodel.SpecialPrintElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.table.TableColumn; import java.awt.*; /** @@ -22,6 +21,5 @@ public interface GraphCellPainter { @Nullable SpecialPrintElement mouseOverArrow(@NotNull GraphPrintCell row, int x, int y); - void setRootColumn(TableColumn column); } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/GraphCommitCell.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/GraphCommitCell.java index ae7d35130f25..f09ceb6ff3b2 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/GraphCommitCell.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/GraphCommitCell.java @@ -1,7 +1,6 @@ package com.intellij.vcs.log.graph.render; import com.intellij.vcs.log.VcsRef; -import com.intellij.vcs.log.printmodel.GraphPrintCell; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -12,15 +11,8 @@ import java.util.Collection; */ public class GraphCommitCell extends CommitCell { - private final GraphPrintCell row; - - public GraphCommitCell(@NotNull GraphPrintCell row, @NotNull String text, - @NotNull Collection refsToThisCommit) { + public GraphCommitCell(@NotNull String text, @NotNull Collection refsToThisCommit) { super(text, refsToThisCommit); - this.row = row; } - public GraphPrintCell getPrintCell() { - return row; - } } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/PositionUtil.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/PositionUtil.java index 025577f7ae72..aab27f879849 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/PositionUtil.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/PositionUtil.java @@ -4,9 +4,10 @@ import com.intellij.vcs.log.graph.elements.Node; import com.intellij.vcs.log.printmodel.GraphPrintCell; import com.intellij.vcs.log.printmodel.ShortEdge; import com.intellij.vcs.log.printmodel.SpecialPrintElement; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.awt.event.MouseEvent; +import java.awt.*; import static com.intellij.vcs.log.graph.render.PrintParameters.*; @@ -15,38 +16,38 @@ public class PositionUtil { return (float)Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } - public static boolean overUpEdge(ShortEdge edge, int x, int y, int offset) { + public static boolean overUpEdge(ShortEdge edge, int x, int y) { float thick = THICK_LINE; - int x1 = WIDTH_NODE * edge.getDownPosition() + WIDTH_NODE / 2 + offset; + int x1 = WIDTH_NODE * edge.getDownPosition() + WIDTH_NODE / 2; int y1 = HEIGHT_CELL / 2; - int x2 = WIDTH_NODE * edge.getUpPosition() + WIDTH_NODE / 2 + offset; + int x2 = WIDTH_NODE * edge.getUpPosition() + WIDTH_NODE / 2; int y2 = -HEIGHT_CELL / 2; //return true; return (distance(x1, y1, x, y) + distance(x2, y2, x, y) < distance(x1, y1, x2, y2) + thick); } - public static boolean overDownEdge(ShortEdge edge, int x, int y, int offset) { + public static boolean overDownEdge(ShortEdge edge, int x, int y) { float thick = THICK_LINE; - int x1 = WIDTH_NODE * edge.getUpPosition() + WIDTH_NODE / 2 + offset; + int x1 = WIDTH_NODE * edge.getUpPosition() + WIDTH_NODE / 2; int y1 = HEIGHT_CELL / 2; - int x2 = WIDTH_NODE * edge.getDownPosition() + WIDTH_NODE / 2 + offset; + int x2 = WIDTH_NODE * edge.getDownPosition() + WIDTH_NODE / 2; int y2 = HEIGHT_CELL + HEIGHT_CELL / 2; 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, int xOffset) { - int x0 = WIDTH_NODE * position + WIDTH_NODE / 2 + xOffset; + public static boolean overNode(int position, int x, int y) { + int x0 = WIDTH_NODE * position + WIDTH_NODE / 2; int y0 = HEIGHT_CELL / 2; int r = CIRCLE_RADIUS; return distance(x0, y0, x, y) <= r; } - public static int getYInsideRow(MouseEvent e) { - return e.getY() - getRowIndex(e) * HEIGHT_CELL; + public static int getYInsideRow(@NotNull Point point) { + return point.y - getRowIndex(point) * HEIGHT_CELL; } - public static int getRowIndex(MouseEvent e) { - return e.getY() / HEIGHT_CELL; + public static int getRowIndex(@NotNull Point point) { + return point.y / HEIGHT_CELL; } @Nullable diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/SimpleGraphCellPainter.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/SimpleGraphCellPainter.java index 4c9c1cff271c..16e2a3115c91 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/SimpleGraphCellPainter.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/render/SimpleGraphCellPainter.java @@ -10,7 +10,6 @@ import com.intellij.vcs.log.printmodel.SpecialPrintElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.table.TableColumn; import java.awt.*; import java.awt.geom.Ellipse2D; @@ -20,8 +19,6 @@ import java.awt.geom.Ellipse2D; public class SimpleGraphCellPainter implements GraphCellPainter { private static final Color MARK_COLOR = JBColor.BLACK; - private final boolean myMultiRoot; - private TableColumn myRootColumn; private Graphics2D g2; @@ -30,10 +27,6 @@ public class SimpleGraphCellPainter implements GraphCellPainter { private final Stroke selectUsual = new BasicStroke(PrintParameters.SELECT_THICK_LINE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL); private final Stroke selectHide = new BasicStroke(PrintParameters.SELECT_THICK_LINE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL, 0, new float[]{7}, 0); - public SimpleGraphCellPainter(boolean multiRoot) { - myMultiRoot = multiRoot; - } - private void paintUpLine(int from, int to, Color color) { int x1 = PrintParameters.WIDTH_NODE * from + PrintParameters.WIDTH_NODE / 2; int y1 = PrintParameters.HEIGHT_CELL / 2; @@ -143,7 +136,7 @@ public class SimpleGraphCellPainter implements GraphCellPainter { this.g2 = g2; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); for (final ShortEdge edge : row.getUpEdges()) { - drawLogic(edge.isSelected(), edge.isMarked(), edge.isUsual(), ColorGenerator.getColor(edge.getEdge().getBranch()), new LitePrinter() { + drawLogic(edge.isSelected(), edge.isMarked(), edge.isUsual(), ColorGenerator.getColor(edge.getEdge().getBranch().getBranchNumber()), new LitePrinter() { @Override public void print(Color color) { paintUpLine(edge.getDownPosition(), edge.getUpPosition(), color); @@ -151,7 +144,7 @@ public class SimpleGraphCellPainter implements GraphCellPainter { }); } for (final ShortEdge edge : row.getDownEdges()) { - drawLogic(edge.isSelected(), edge.isMarked(), edge.isUsual(), ColorGenerator.getColor(edge.getEdge().getBranch()), new LitePrinter() { + drawLogic(edge.isSelected(), edge.isMarked(), edge.isUsual(), ColorGenerator.getColor(edge.getEdge().getBranch().getBranchNumber()), new LitePrinter() { @Override public void print(Color color) { paintDownLine(edge.getUpPosition(), edge.getDownPosition(), color); @@ -167,16 +160,16 @@ public class SimpleGraphCellPainter implements GraphCellPainter { assert node != null; if (printElement.isSelected()) { paintCircle(printElement.getPosition(), MARK_COLOR, true); - paintCircle(printElement.getPosition(), ColorGenerator.getColor(node.getBranch()), false); + paintCircle(printElement.getPosition(), ColorGenerator.getColor(node.getBranch().getBranchNumber()), false); } else { - paintCircle(printElement.getPosition(), ColorGenerator.getColor(node.getBranch()), printElement.isMarked()); + paintCircle(printElement.getPosition(), ColorGenerator.getColor(node.getBranch().getBranchNumber()), printElement.isMarked()); } break; case UP_ARROW: edge = printElement.getGraphElement().getEdge(); assert edge != null; drawLogic(printElement.isSelected(), printElement.isMarked(), edge.getType() == Edge.EdgeType.USUAL, ColorGenerator.getColor( - edge.getBranch()), new LitePrinter() { + edge.getBranch().getBranchNumber()), new LitePrinter() { @Override public void print(Color color) { paintShow(printElement.getPosition(), color); @@ -187,7 +180,7 @@ public class SimpleGraphCellPainter implements GraphCellPainter { edge = printElement.getGraphElement().getEdge(); assert edge != null; drawLogic(printElement.isSelected(), printElement.isMarked(), edge.getType() == Edge.EdgeType.USUAL, ColorGenerator.getColor( - edge.getBranch()), new LitePrinter() { + edge.getBranch().getBranchNumber()), new LitePrinter() { @Override public void print(Color color) { paintHide(printElement.getPosition(), color); @@ -204,9 +197,9 @@ public class SimpleGraphCellPainter implements GraphCellPainter { Node node = printElement.getGraphElement().getNode(); assert node != null; if (printElement.getDragAndDropSelect() > 0) { - paintAbove(printElement.getPosition(), ColorGenerator.getColor(node.getBranch())); + paintAbove(printElement.getPosition(), ColorGenerator.getColor(node.getBranch().getBranchNumber())); } else { - paintBelow(printElement.getPosition(), ColorGenerator.getColor(node.getBranch())); + paintBelow(printElement.getPosition(), ColorGenerator.getColor(node.getBranch().getBranchNumber())); } } } @@ -217,18 +210,18 @@ public class SimpleGraphCellPainter implements GraphCellPainter { public GraphElement mouseOver(@NotNull GraphPrintCell row, int x, int y) { for (SpecialPrintElement printElement : row.getSpecialPrintElements()) { if (printElement.getType() == SpecialPrintElement.Type.COMMIT_NODE) { - if (PositionUtil.overNode(printElement.getPosition(), x, y, getXOffset())) { + if (PositionUtil.overNode(printElement.getPosition(), x, y)) { return printElement.getGraphElement(); } } } for (ShortEdge edge : row.getUpEdges()) { - if (PositionUtil.overUpEdge(edge, x, y, getXOffset())) { + if (PositionUtil.overUpEdge(edge, x, y)) { return edge.getEdge(); } } for (ShortEdge edge : row.getDownEdges()) { - if (PositionUtil.overDownEdge(edge, x, y, getXOffset())) { + if (PositionUtil.overDownEdge(edge, x, y)) { return edge.getEdge(); } } @@ -236,16 +229,12 @@ public class SimpleGraphCellPainter implements GraphCellPainter { return null; } - private int getXOffset() { - return myMultiRoot && myRootColumn != null ? myRootColumn.getWidth() : 0; - } - @Nullable @Override public SpecialPrintElement mouseOverArrow(@NotNull GraphPrintCell row, int x, int y) { for (SpecialPrintElement printElement : row.getSpecialPrintElements()) { if (printElement.getType() != SpecialPrintElement.Type.COMMIT_NODE) { - if (PositionUtil.overNode(printElement.getPosition(), x, y, getXOffset())) { + if (PositionUtil.overNode(printElement.getPosition(), x, y)) { return printElement; } } @@ -253,8 +242,4 @@ public class SimpleGraphCellPainter implements GraphCellPainter { return null; } - @Override - public void setRootColumn(TableColumn column) { - myRootColumn = column; - } } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/GraphFlags.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/GraphFlags.java new file mode 100644 index 000000000000..568c478235af --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/GraphFlags.java @@ -0,0 +1,79 @@ +/* + * 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.vcs.log.newgraph; + +import com.intellij.vcs.log.newgraph.utils.Flags; + +public class GraphFlags { + private final int myNodesCount; + private final byte[] myFlags; + + public GraphFlags(int nodesCount) { + myNodesCount = nodesCount; + myFlags = new byte[nodesCount]; + } + + private Flags getFlags(int offset) { + assert offset >= 0 && offset < 8; + final int mask = 1 << offset; + return new Flags() { + @Override + public int size() { + return myNodesCount; + } + + @Override + public boolean get(int index) { + return (myFlags[index] & mask) != 0; + } + + @Override + public void set(int index, boolean value) { + if (value) { + myFlags[index] |= mask; + } else { + myFlags[index] &= ~mask; + } + } + }; + } + + public Flags getVisibleNodes() { + return getFlags(1); + } + + public Flags getVisibleNodesInBranches() { + return getFlags(3); + } + + // From node k exist only one edge to k + 1 + public Flags getSimpleNodeFlags() { + return getFlags(0); + } + + public Flags getThickFlags() { + return getFlags(2); + } + + public Flags getFlagsForFilters() { + return getFlags(4); + } + + public Flags getTempFlags() { + return getFlags(5); + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/PermanentGraph.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/PermanentGraph.java new file mode 100644 index 000000000000..d3a949ff02ee --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/PermanentGraph.java @@ -0,0 +1,26 @@ +/* + * 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.vcs.log.newgraph; + +/** + * @author erokhins + */ +public interface PermanentGraph extends SomeGraph { + int NOT_LOAD_COMMIT = Integer.MIN_VALUE; + + // if nodeIndex == nodesCount(), returned NOT_LOAD_COMMIT + int getHashIndex(int nodeIndex); +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/PermanentGraphLayout.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/PermanentGraphLayout.java new file mode 100644 index 000000000000..32cba2b00fae --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/PermanentGraphLayout.java @@ -0,0 +1,32 @@ +/* + * 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.vcs.log.newgraph; + +/** + * @author erokhins + */ +public interface PermanentGraphLayout { + // nodeIndex must be in [0..graph.nodesCount() - 1] + + + int getLayoutIndex(int nodeIndex); + int getOneOfHeadNodeIndex(int nodeIndex); + + int getHeadNodeIndex(int layoutIndex); + + // return layout index head node + int getStartLayout(int layoutIndex); +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/SomeGraph.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/SomeGraph.java new file mode 100644 index 000000000000..cf1a7956679e --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/SomeGraph.java @@ -0,0 +1,39 @@ +/* + * 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.vcs.log.newgraph; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface SomeGraph { + int NOT_LOAD_COMMIT = Integer.MAX_VALUE; + + int nodesCount(); + + @NotNull + List getUpNodes(int nodeIndex); + + /** + * + * @param nodeIndex + * @return list adjacent nodes, which index > nodeIndex. + * If one of adjacent node wasn't load, it nodeIndex = NOT_LOAD_COMMIT + */ + @NotNull + List getDownNodes(int nodeIndex); + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/ContainingBranchesGetter.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/ContainingBranchesGetter.java new file mode 100644 index 000000000000..9876c3848661 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/ContainingBranchesGetter.java @@ -0,0 +1,79 @@ +/* + * 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.vcs.log.newgraph.facade; + +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.utils.DfsUtil; +import com.intellij.vcs.log.newgraph.utils.Flags; +import com.intellij.vcs.log.newgraph.utils.MyUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +public class ContainingBranchesGetter { + @NotNull + private final PermanentGraph myPermanentGraph; + + @NotNull + private final Set myBranchNodeIndexes; + + @NotNull + private final DfsUtil myDfsUtil; + + @NotNull + private final Flags myTempFlags; + + public ContainingBranchesGetter(@NotNull PermanentGraph permanentGraph, + @NotNull Set branchNodeIndexes, + @NotNull DfsUtil dfsUtil, + @NotNull Flags tempFlags) { + myPermanentGraph = permanentGraph; + myBranchNodeIndexes = branchNodeIndexes; + myDfsUtil = dfsUtil; + myTempFlags = tempFlags; + } + + public Set getBranchHashIndexes(int nodeIndex) { + final Set result = new HashSet(); + + MyUtils.setAllValues(myTempFlags, false); + myTempFlags.set(nodeIndex, true); + checkAndAdd(nodeIndex, result); + myDfsUtil.nodeDfsIterator(nodeIndex, new DfsUtil.NextNode() { + @Override + public int fun(int currentNode) { + for (int upNode : myPermanentGraph.getUpNodes(currentNode)) { + if (!myTempFlags.get(upNode)) { + myTempFlags.set(upNode, true); + checkAndAdd(upNode, result); + return upNode; + } + } + + return NODE_NOT_FOUND; + } + }); + + return result; + } + + private void checkAndAdd(int nodeIndex, Set result) { + if (myBranchNodeIndexes.contains(nodeIndex)) + result.add(myPermanentGraph.getHashIndex(nodeIndex)); + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/CurrentBranches.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/CurrentBranches.java new file mode 100644 index 000000000000..947a569f7076 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/CurrentBranches.java @@ -0,0 +1,78 @@ +/* + * 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.vcs.log.newgraph.facade; + +import com.intellij.util.containers.HashSet; +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.SomeGraph; +import com.intellij.vcs.log.newgraph.utils.DfsUtil; +import com.intellij.vcs.log.newgraph.utils.Flags; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +import static com.intellij.vcs.log.newgraph.utils.MyUtils.setAllValues; + +public class CurrentBranches { + private final PermanentGraph myPermanentGraph; + private final Flags myVisibleNodesInBranches; + private final DfsUtil myDfsUtil; + + public CurrentBranches(PermanentGraph permanentGraph, Flags visibleNodesInBranches, DfsUtil dfsUtil) { + myPermanentGraph = permanentGraph; + myVisibleNodesInBranches = visibleNodesInBranches; + myDfsUtil = dfsUtil; + setVisibleBranches(null); + } + + public Flags getVisibleNodesInBranches() { + return myVisibleNodesInBranches; + } + + public void setVisibleBranches(@Nullable Set heads) { + if (heads == null) { + setAllValues(myVisibleNodesInBranches, true); + return; + } + setAllValues(myVisibleNodesInBranches, false); + Set startedNodes = new HashSet(); + for (int i = 0; i < myPermanentGraph.nodesCount(); i++) { + if (heads.contains(myPermanentGraph.getHashIndex(i))) + startedNodes.add(i); + } + assert !startedNodes.isEmpty(); + selectAllVisibleNodes(startedNodes); + } + + private void selectAllVisibleNodes(@NotNull Set startedNodes) { + for (int startNode : startedNodes) { + myVisibleNodesInBranches.set(startNode, true); + myDfsUtil.nodeDfsIterator(startNode, new DfsUtil.NextNode() { + @Override + public int fun(int currentNode) { + for (int downNode : myPermanentGraph.getDownNodes(currentNode)) { + if (downNode != SomeGraph.NOT_LOAD_COMMIT && !myVisibleNodesInBranches.get(downNode)) { + myVisibleNodesInBranches.set(downNode, true); + return downNode; + } + } + return NODE_NOT_FOUND; + } + }); + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/GraphActionDispatcher.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/GraphActionDispatcher.java new file mode 100644 index 000000000000..3e98432ea6cc --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/GraphActionDispatcher.java @@ -0,0 +1,205 @@ +/* + * 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.vcs.log.newgraph.facade; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Pair; +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.graph.*; +import com.intellij.vcs.log.newgraph.SomeGraph; +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import com.intellij.vcs.log.newgraph.gpaph.actions.*; +import com.intellij.vcs.log.newgraph.render.cell.GraphCell; +import com.intellij.vcs.log.newgraph.render.cell.SpecialRowElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; + +public class GraphActionDispatcher { + + private static final Logger LOG = Logger.getInstance(GraphActionDispatcher.class); + + @NotNull + private final GraphData myGraphData; + + public GraphActionDispatcher(@NotNull GraphData graphData) { + myGraphData = graphData; + } + + @Nullable + public GraphAnswer performAction(@NotNull GraphAction action) { + if (action instanceof LongEdgesAction) { + myGraphData.getGraphRender().setShowLongEdges(((LongEdgesAction)action).shouldShowLongEdges()); + } + + if (action instanceof MouseOverAction) { + return mouseOverAction((MouseOverAction)action); + } + + if (action instanceof ClickGraphAction) { + return clickToGraph((ClickGraphAction)action); + } + + if (action instanceof LinearBranchesExpansionAction) { + myGraphData.getMutableGraph().performAction(new LinearBranchesExpansionInternalGraphAction(((LinearBranchesExpansionAction)action).shouldExpand())); + myGraphData.getGraphRender().invalidate(); + return ActionRequestGraphAnswer.JUMP_TO_FIRST_ROW; + } + + if (action instanceof SelectAllRelativeCommitsAction) { + selectAllRelativeCommits(((SelectAllRelativeCommitsAction)action)); + } + + return null; + } + + private void selectAllRelativeCommits(@NotNull SelectAllRelativeCommitsAction action) { + InternalGraphAction internalGraphAction; + + if (action == SelectAllRelativeCommitsAction.DESELECT_ALL) { + internalGraphAction = SelectAllRelativeCommitsInternalGraphAction.DESELECT_ALL; + } else { + int visibleRowIndex = action.getVisibleRowIndex(); + myGraphData.assertRange(visibleRowIndex); + internalGraphAction = new SelectAllRelativeCommitsInternalGraphAction(visibleRowIndex); + } + myGraphData.getMutableGraph().performAction(internalGraphAction); + } + + @Nullable + private GraphAnswer clickToGraph(@NotNull ClickGraphAction action) { + int visibleRowIndex = action.getRow(); + if (visibleRowIndex >= myGraphData.getMutableGraph().getCountVisibleNodes()) + return null; + + Point clickPoint = action.getRelativePoint(); + if (clickPoint == null) { + return null; + } + + Pair arrowOrGraphElement = getOverArrowOrGraphElement(visibleRowIndex, clickPoint); + if (arrowOrGraphElement.first != null) { + int toRow; + Edge edge = (Edge)arrowOrGraphElement.first.getElement(); + + if (arrowOrGraphElement.first.getType() == SpecialRowElement.Type.DOWN_ARROW) + toRow = edge.getDownNodeVisibleIndex(); + else + toRow = edge.getUpNodeVisibleIndex(); + + if (toRow == SomeGraph.NOT_LOAD_COMMIT) { + int indexOfParentCommit = myGraphData.getMutableGraph().getNode(edge.getUpNodeVisibleIndex()).getDownEdges().indexOf(edge); + + int indexInPermanentGraph = myGraphData.getMutableGraph().getIndexInPermanentGraph(edge.getUpNodeVisibleIndex()); + int commitHashIndex = myGraphData.getPermanentGraph().getHashIndex(indexInPermanentGraph); + + GraphCommit commit = myGraphData.getCommitsWithNotLoadParentMap().get(commitHashIndex); + if (indexOfParentCommit >= 0 && indexOfParentCommit < commit.getParentIndices().length) { + return ActionRequestGraphAnswer.jumpToNotLoadCommit(commit.getParentIndices()[indexOfParentCommit]); + } else { + LOG.error("Jump to not load commit with bad edge index. " + + "Edge index: " + indexOfParentCommit + ", commit hash index: " + commitHashIndex + + "count parents commits: " + commit.getParentIndices().length + "."); + return null; + } + } + + return ActionRequestGraphAnswer.jumpToRow(toRow); + } else { + int toRow = myGraphData.getMutableGraph().performAction(new ClickInternalGraphAction(arrowOrGraphElement.second)); + myGraphData.getMutableGraph().performAction(new MouseOverGraphElementInternalGraphAction(null)); + myGraphData.getGraphRender().invalidate(); + if (toRow != -1) + return ActionRequestGraphAnswer.jumpToRow(toRow); + return null; // TODO + } + } + + @Nullable + private GraphAnswer mouseOverAction(@NotNull MouseOverAction action) { + int visibleRowIndex = action.getRow(); + if (visibleRowIndex >= myGraphData.getMutableGraph().getCountVisibleNodes()) + return null; + + Pair arrowOrGraphElement = getOverArrowOrGraphElement(visibleRowIndex, action.getRelativePoint()); + if (arrowOrGraphElement.first != null) { + myGraphData.getMutableGraph().performAction(new MouseOverGraphElementInternalGraphAction(null)); + myGraphData.getMutableGraph().performAction(new MouseOverArrowInternalGraphAction(arrowOrGraphElement.first)); + return ActionRequestGraphAnswer.CHANGE_CURSOR_TO_HAND_CURSOR; + } else { + myGraphData.getMutableGraph().performAction(new MouseOverArrowInternalGraphAction(null)); + myGraphData.getMutableGraph().performAction(new MouseOverGraphElementInternalGraphAction(arrowOrGraphElement.second)); + return ActionRequestGraphAnswer.CHANGE_CURSOR_TO_DEFAULT; + } + } + + + @NotNull + private Pair getOverArrowOrGraphElement(int visibleRowIndex, @NotNull Point point) { + GraphCell graphCell = myGraphData.getGraphRender().getCellGenerator().getGraphCell(visibleRowIndex); + + SpecialRowElement overArrow = myGraphData.getGraphRender().getCellPainter().mouseOverArrow(graphCell, point.x, point.y); + GraphElement overElement = myGraphData.getGraphRender().getCellPainter().mouseOver(graphCell, point.x, point.y); + return new Pair(overArrow, overElement); + } + + + private static class ActionRequestGraphAnswer implements GraphAnswer { + + public final static GraphAnswer CHANGE_CURSOR_TO_HAND_CURSOR = changeCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + public final static GraphAnswer CHANGE_CURSOR_TO_DEFAULT = changeCursor(Cursor.getDefaultCursor()); + + public final static GraphAnswer JUMP_TO_FIRST_ROW = jumpToRow(0); + + @NotNull + public static GraphAnswer jumpToRow(int rowIndex) { + return new ActionRequestGraphAnswer(new JumpToRowActionRequest(rowIndex)); + } + + @NotNull + public static GraphAnswer jumpToNotLoadCommit(int commitHashIndex) { + return new ActionRequestGraphAnswer(new JumpToNotLoadCommitActionRequest(commitHashIndex)); + } + + @NotNull + public static GraphAnswer changeCursor(@NotNull Cursor cursor) { + return new ActionRequestGraphAnswer(new ChangeCursorActionRequest(cursor)); + } + + @NotNull + private final GraphActionRequest myGraphActionRequest; + + private ActionRequestGraphAnswer(@NotNull GraphActionRequest graphActionRequest) { + myGraphActionRequest = graphActionRequest; + } + + @Nullable + @Override + public GraphChange getGraphChange() { + return null; + } + + @Nullable + @Override + public GraphActionRequest getActionRequest() { + return myGraphActionRequest; + } + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/GraphData.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/GraphData.java new file mode 100644 index 000000000000..34c6c0d3ccb0 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/GraphData.java @@ -0,0 +1,167 @@ +/* + * 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.vcs.log.newgraph.facade; + +import com.intellij.openapi.util.Condition; +import com.intellij.util.containers.HashSet; +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.newgraph.GraphFlags; +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.PermanentGraphLayout; +import com.intellij.vcs.log.newgraph.gpaph.MutableGraph; +import com.intellij.vcs.log.newgraph.gpaph.impl.CollapsedMutableGraph; +import com.intellij.vcs.log.newgraph.gpaph.impl.FilterMutableGraph; +import com.intellij.vcs.log.newgraph.render.ElementColorManager; +import com.intellij.vcs.log.newgraph.render.GraphRender; +import com.intellij.vcs.log.newgraph.render.cell.FilterGraphCellGenerator; +import com.intellij.vcs.log.newgraph.render.cell.GraphCellGeneratorImpl; +import com.intellij.vcs.log.newgraph.utils.DfsUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public class GraphData { + + @NotNull + private final GraphFlags myGraphFlags; + + @NotNull + private final PermanentGraph myPermanentGraph; + + @NotNull + private final Map myCommitsWithNotLoadParentMap; + + @NotNull + private final PermanentGraphLayout myPermanentGraphLayout; + + @NotNull + private final DfsUtil myDfsUtil; + + @NotNull + private final ElementColorManager myColorManager; + + // contains node index in permanent graph + @NotNull + private final Set myBranchNodeIndexes; + + @NotNull + private final CurrentBranches myCurrentBranches; + + @NotNull + private final ContainingBranchesGetter myContainingBranchesGetter; + + @NotNull + private MutableGraph myMutableGraph; + + @NotNull + private GraphRender myGraphRender; + + @Nullable + private Set myHeads = null; + + @Nullable + private Condition myVisibilityPredicate = null; + + public GraphData(@NotNull GraphFlags graphFlags, + @NotNull PermanentGraph permanentGraph, @NotNull Map commitsWithNotLoadParentMap, @NotNull PermanentGraphLayout permanentGraphLayout, + @NotNull ElementColorManager colorManager, + @NotNull DfsUtil dfsUtil, @NotNull Set branchNodeIndexes) { + myGraphFlags = graphFlags; + myPermanentGraph = permanentGraph; + myCommitsWithNotLoadParentMap = commitsWithNotLoadParentMap; + myPermanentGraphLayout = permanentGraphLayout; + myColorManager = colorManager; + myDfsUtil = dfsUtil; + myBranchNodeIndexes = branchNodeIndexes; + myCurrentBranches = new CurrentBranches(permanentGraph, graphFlags.getVisibleNodesInBranches(), dfsUtil); + myContainingBranchesGetter = new ContainingBranchesGetter(myPermanentGraph, branchNodeIndexes, dfsUtil, graphFlags.getTempFlags()); + applyFilters(); + } + + public void setVisibleBranches(@Nullable Collection heads) { + myHeads = heads == null ? null : new HashSet(heads); + applyFilters(); + } + + public void setFilter(@Nullable Condition visibilityPredicate) { + myVisibilityPredicate = visibilityPredicate; + applyFilters(); + } + + private void applyFilters() { + myCurrentBranches.setVisibleBranches(myHeads); + if (myVisibilityPredicate == null) { + myMutableGraph = CollapsedMutableGraph.newInstance(myPermanentGraph, myPermanentGraphLayout, myGraphFlags, myBranchNodeIndexes, myDfsUtil); + GraphCellGeneratorImpl cellGenerator = new GraphCellGeneratorImpl(myMutableGraph); + myGraphRender = new GraphRender(myMutableGraph, myColorManager, cellGenerator); + } else { + Condition isVisibleNode = new Condition() { + @Override + public boolean value(Integer integer) { + return myVisibilityPredicate.value(myPermanentGraph.getHashIndex(integer)); + } + }; + FilterMutableGraph filterMutableGraph = + FilterMutableGraph.newInstance(myPermanentGraph, myPermanentGraphLayout, myGraphFlags.getVisibleNodesInBranches(), myGraphFlags.getFlagsForFilters(), isVisibleNode); + myMutableGraph = filterMutableGraph; + FilterGraphCellGenerator filterGraphCellGenerator = new FilterGraphCellGenerator(filterMutableGraph); + myGraphRender = new GraphRender(myMutableGraph, myColorManager, filterGraphCellGenerator); + } + } + + @NotNull + public PermanentGraph getPermanentGraph() { + return myPermanentGraph; + } + + @NotNull + public Map getCommitsWithNotLoadParentMap() { + return myCommitsWithNotLoadParentMap; + } + + @NotNull + public PermanentGraphLayout getPermanentGraphLayout() { + return myPermanentGraphLayout; + } + + @NotNull + public ContainingBranchesGetter getContainingBranchesGetter() { + return myContainingBranchesGetter; + } + + @NotNull + public MutableGraph getMutableGraph() { + return myMutableGraph; + } + + @NotNull + public GraphRender getGraphRender() { + return myGraphRender; + } + + public int getCountVisibleNodes() { + return getMutableGraph().getCountVisibleNodes(); + } + + public void assertRange(int visibleRowIndex) { + if (visibleRowIndex < 0 || visibleRowIndex >= getCountVisibleNodes()) { + throw new IllegalArgumentException("Row not exist! Request row index: " + visibleRowIndex + ", count rows: " + getCountVisibleNodes()); + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/GraphFacadeImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/GraphFacadeImpl.java new file mode 100644 index 000000000000..ee36fe0adb50 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/facade/GraphFacadeImpl.java @@ -0,0 +1,166 @@ +/* + * 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.vcs.log.newgraph.facade; + +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Pair; +import com.intellij.ui.JBColor; +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.graph.*; +import com.intellij.vcs.log.newgraph.GraphFlags; +import com.intellij.vcs.log.newgraph.PermanentGraphLayout; +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import com.intellij.vcs.log.newgraph.impl.PermanentGraphBuilder; +import com.intellij.vcs.log.newgraph.impl.PermanentGraphImpl; +import com.intellij.vcs.log.newgraph.impl.PermanentGraphLayoutBuilder; +import com.intellij.vcs.log.newgraph.render.ElementColorManager; +import com.intellij.vcs.log.newgraph.utils.DfsUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class GraphFacadeImpl implements GraphFacade { + @NotNull + public static GraphFacadeImpl newInstance(@NotNull List commits, + @NotNull Set branchCommitHashIndexes, + @NotNull final GraphColorManager colorManager) { + GraphFlags flags = new GraphFlags(commits.size()); + Pair> graphAndUnderdoneCommits = PermanentGraphBuilder.build(flags.getSimpleNodeFlags(), commits); + final PermanentGraphImpl permanentGraph = graphAndUnderdoneCommits.first; + + DfsUtil dfsUtil = new DfsUtil(commits.size()); + + final PermanentGraphLayout graphLayout = PermanentGraphLayoutBuilder.build(dfsUtil, permanentGraph, new Comparator() { + @Override + public int compare(@NotNull Integer o1, @NotNull Integer o2) { + int hashIndex1 = permanentGraph.getHashIndex(o1); + int hashIndex2 = permanentGraph.getHashIndex(o2); + return colorManager.compareHeads(hashIndex1, hashIndex2); + } + }); + + ElementColorManager elementColorManager = new ElementColorManager() { + @NotNull + @Override + public JBColor getColor(@NotNull GraphElement element) { + int headNodeIndex = graphLayout.getHeadNodeIndex(element.getLayoutIndex()); + int headHashIndex = permanentGraph.getHashIndex(headNodeIndex); + int baseLayoutIndex = graphLayout.getStartLayout(element.getLayoutIndex()); + if (baseLayoutIndex == element.getLayoutIndex()) { + return colorManager.getColorOfBranch(headHashIndex); + } else { + return colorManager.getColorOfFragment(headHashIndex, element.getLayoutIndex()); + } + } + }; + + Set branchNodeIndexes = new HashSet(); + for (int i = 0; i < permanentGraph.nodesCount(); i++) { + if (branchCommitHashIndexes.contains(permanentGraph.getHashIndex(i))) + branchNodeIndexes.add(i); + } + GraphData graphData = new GraphData(flags, permanentGraph, graphAndUnderdoneCommits.second, graphLayout, elementColorManager, dfsUtil, + branchNodeIndexes); + return new GraphFacadeImpl(graphData); + } + + @NotNull + private final GraphData myGraphData; + + @NotNull + private final GraphActionDispatcher myActionDispatcher; + + public GraphFacadeImpl(@NotNull GraphData graphData) { + myGraphData = graphData; + myActionDispatcher = new GraphActionDispatcher(graphData); + } + + @NotNull + @Override + public PaintInfo paint(int visibleRow) { + myGraphData.assertRange(visibleRow); + return myGraphData.getGraphRender().paint(visibleRow); + } + + @Nullable + @Override + public GraphAnswer performAction(@NotNull GraphAction action) { + return myActionDispatcher.performAction(action); + } + + @NotNull + @Override + public List getAllCommits() { + throw new UnsupportedOperationException(); + } + + @Override + public int getCommitAtRow(int visibleRow) { + myGraphData.assertRange(visibleRow); + int indexInPermanentGraph = myGraphData.getMutableGraph().getIndexInPermanentGraph(visibleRow); + return myGraphData.getPermanentGraph().getHashIndex(indexInPermanentGraph); + } + + @Override + public int getVisibleCommitCount() { + return myGraphData.getCountVisibleNodes(); + } + + @Override + public void setVisibleBranches(@Nullable Collection heads) { + myGraphData.setVisibleBranches(heads); + } + + @Override + public void setFilter(@Nullable Condition visibilityPredicate) { + myGraphData.setFilter(visibilityPredicate); + } + + @NotNull + @Override + public GraphInfoProvider getInfoProvider() { + return new GraphInfoProvider() { + @NotNull + @Override + public Set getContainingBranches(int visibleRow) { + myGraphData.assertRange(visibleRow); + return myGraphData.getContainingBranchesGetter() + .getBranchHashIndexes(myGraphData.getMutableGraph().getIndexInPermanentGraph(visibleRow)); + } + + @NotNull + @Override + public RowInfo getRowInfo(int visibleRow) { + myGraphData.assertRange(visibleRow); + int indexInPermanentGraph = myGraphData.getMutableGraph().getIndexInPermanentGraph(visibleRow); + final int oneOfHeadNodeIndex = myGraphData.getPermanentGraphLayout().getOneOfHeadNodeIndex(indexInPermanentGraph); + return new RowInfo() { + @Override + public int getOneOfHeads() { + return myGraphData.getPermanentGraph().getHashIndex(oneOfHeadNodeIndex); + } + }; + } + + @Override + public boolean areLongEdgesHidden() { + return !myGraphData.getGraphRender().isShowLongEdges(); + } + }; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/Edge.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/Edge.java new file mode 100644 index 000000000000..0a7fcc0cc4cb --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/Edge.java @@ -0,0 +1,34 @@ +/* + * 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.vcs.log.newgraph.gpaph; + +import org.jetbrains.annotations.NotNull; + +public interface Edge extends GraphElement { + + int getUpNodeVisibleIndex(); + + // sometime may returned SomeGraph.NOT_LOAD_COMMIT + int getDownNodeVisibleIndex(); + + @NotNull + Type getType(); + + enum Type { + USUAL, + HIDE_FRAGMENT + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/GraphElement.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/GraphElement.java new file mode 100644 index 000000000000..385e259d7299 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/GraphElement.java @@ -0,0 +1,22 @@ +/* + * 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.vcs.log.newgraph.gpaph; + +public interface GraphElement { + + int getLayoutIndex(); + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/GraphWithElementsInfo.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/GraphWithElementsInfo.java new file mode 100644 index 000000000000..603da8a4bdbf --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/GraphWithElementsInfo.java @@ -0,0 +1,29 @@ +/* + * 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.vcs.log.newgraph.gpaph; + +import com.intellij.vcs.log.newgraph.SomeGraph; +import org.jetbrains.annotations.NotNull; + +public interface GraphWithElementsInfo extends SomeGraph { + + @NotNull + Node.Type getNodeType(int nodeIndex); + + @NotNull + Edge.Type getEdgeType(int upNodeIndex, int downNodeIndex); + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/MutableGraph.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/MutableGraph.java new file mode 100644 index 000000000000..1a2dfc2a390f --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/MutableGraph.java @@ -0,0 +1,33 @@ +/* + * 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.vcs.log.newgraph.gpaph; + +import com.intellij.vcs.log.newgraph.gpaph.actions.InternalGraphAction; +import org.jetbrains.annotations.NotNull; + +public interface MutableGraph { + int getCountVisibleNodes(); + + @NotNull + Node getNode(int visibleNodeIndex); + + int getIndexInPermanentGraph(int visibleNodeIndex); + + int performAction(@NotNull InternalGraphAction action); + + @NotNull + ThickHoverController getThickHoverController(); +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/Node.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/Node.java new file mode 100644 index 000000000000..a135e45b4e2b --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/Node.java @@ -0,0 +1,39 @@ +/* + * 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.vcs.log.newgraph.gpaph; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface Node extends GraphElement { + + int getVisibleNodeIndex(); + + @NotNull + Type getType(); + + @NotNull + List getUpEdges(); + + @NotNull + List getDownEdges(); + + enum Type { + USUAL, + CHANGED // for interactive rebase + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/ThickHoverController.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/ThickHoverController.java new file mode 100644 index 000000000000..6c8a36a70933 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/ThickHoverController.java @@ -0,0 +1,27 @@ +/* + * 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.vcs.log.newgraph.gpaph; + +import com.intellij.vcs.log.newgraph.render.cell.SpecialRowElement; +import org.jetbrains.annotations.NotNull; + +public interface ThickHoverController { + + boolean isThick(@NotNull GraphElement element); + boolean isHover(@NotNull GraphElement element); + + boolean isHover(@NotNull SpecialRowElement specialRowElement); +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/ClickInternalGraphAction.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/ClickInternalGraphAction.java new file mode 100644 index 000000000000..f10608155c3f --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/ClickInternalGraphAction.java @@ -0,0 +1,25 @@ +/* + * 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.vcs.log.newgraph.gpaph.actions; + +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import org.jetbrains.annotations.Nullable; + +public class ClickInternalGraphAction extends InternalGraphActionWithSomeInfo { + public ClickInternalGraphAction(@Nullable GraphElement graphElement) { + super(graphElement); + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/InternalGraphAction.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/InternalGraphAction.java new file mode 100644 index 000000000000..fdf21565deb0 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/InternalGraphAction.java @@ -0,0 +1,19 @@ +/* + * 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.vcs.log.newgraph.gpaph.actions; + +public interface InternalGraphAction { +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/InternalGraphActionWithSomeInfo.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/InternalGraphActionWithSomeInfo.java new file mode 100644 index 000000000000..e8ae54b0f573 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/InternalGraphActionWithSomeInfo.java @@ -0,0 +1,32 @@ +/* + * 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.vcs.log.newgraph.gpaph.actions; + +import org.jetbrains.annotations.Nullable; + +public class InternalGraphActionWithSomeInfo implements InternalGraphAction { + @Nullable + private final I info; + + public InternalGraphActionWithSomeInfo(@Nullable I info) { + this.info = info; + } + + @Nullable + public I getInfo() { + return info; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/LinearBranchesExpansionInternalGraphAction.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/LinearBranchesExpansionInternalGraphAction.java new file mode 100644 index 000000000000..2cbdd9e19898 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/LinearBranchesExpansionInternalGraphAction.java @@ -0,0 +1,25 @@ +/* + * 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.vcs.log.newgraph.gpaph.actions; + +import org.jetbrains.annotations.Nullable; + +public class LinearBranchesExpansionInternalGraphAction extends InternalGraphActionWithSomeInfo { + public LinearBranchesExpansionInternalGraphAction(@Nullable Boolean info) { + super(info); + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/MouseOverArrowInternalGraphAction.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/MouseOverArrowInternalGraphAction.java new file mode 100644 index 000000000000..0010a1559e34 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/MouseOverArrowInternalGraphAction.java @@ -0,0 +1,26 @@ +/* + * 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.vcs.log.newgraph.gpaph.actions; + +import com.intellij.vcs.log.newgraph.render.cell.SpecialRowElement; +import org.jetbrains.annotations.Nullable; + +public class MouseOverArrowInternalGraphAction extends InternalGraphActionWithSomeInfo { + public MouseOverArrowInternalGraphAction(@Nullable SpecialRowElement info) { + super(info); + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/MouseOverGraphElementInternalGraphAction.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/MouseOverGraphElementInternalGraphAction.java new file mode 100644 index 000000000000..bf280378d51c --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/MouseOverGraphElementInternalGraphAction.java @@ -0,0 +1,25 @@ +/* + * 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.vcs.log.newgraph.gpaph.actions; + +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import org.jetbrains.annotations.Nullable; + +public class MouseOverGraphElementInternalGraphAction extends InternalGraphActionWithSomeInfo { + public MouseOverGraphElementInternalGraphAction(@Nullable GraphElement graphElement) { + super(graphElement); + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/SelectAllRelativeCommitsInternalGraphAction.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/SelectAllRelativeCommitsInternalGraphAction.java new file mode 100644 index 000000000000..2cee1b3031a9 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/actions/SelectAllRelativeCommitsInternalGraphAction.java @@ -0,0 +1,26 @@ +/* + * 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.vcs.log.newgraph.gpaph.actions; + +import org.jetbrains.annotations.Nullable; + +public class SelectAllRelativeCommitsInternalGraphAction extends InternalGraphActionWithSomeInfo { + public static final SelectAllRelativeCommitsInternalGraphAction DESELECT_ALL = new SelectAllRelativeCommitsInternalGraphAction(null); + + public SelectAllRelativeCommitsInternalGraphAction(@Nullable Integer visibleRowIndex) { + super(visibleRowIndex); + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/fragments/FragmentGenerator.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/fragments/FragmentGenerator.java new file mode 100644 index 000000000000..f6602b356df7 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/fragments/FragmentGenerator.java @@ -0,0 +1,204 @@ +/* + * 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.vcs.log.newgraph.gpaph.fragments; + +import com.intellij.openapi.util.Pair; +import com.intellij.util.Function; +import com.intellij.vcs.log.newgraph.SomeGraph; +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import com.intellij.vcs.log.newgraph.gpaph.Node; +import com.intellij.vcs.log.newgraph.gpaph.impl.CollapsedMutableGraph; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class FragmentGenerator { + private static final int SHORT_FRAGMENT_MAX_SIZE = 100; + private static final int MAX_SEARCH_SIZE = 10; + + @NotNull + private final CollapsedMutableGraph myMutableGraph; + + @NotNull + private final Set myBranchNodeIndexes; + + private final Function> upNodesFun = new Function>() { + @Override + public List fun(Integer integer) { + return myMutableGraph.getInternalGraph().getUpNodes(integer); + } + }; + + private final Function> downNodesFun = new Function>() { + @Override + public List fun(Integer integer) { + return myMutableGraph.getInternalGraph().getDownNodes(integer); + } + }; + + private final Function thisNodeCantBeInMiddle = new Function() { + @Override + public Boolean fun(Integer integer) { + return myBranchNodeIndexes.contains(integer); + } + }; + + + public FragmentGenerator(@NotNull CollapsedMutableGraph mutableGraph, @NotNull Set branchNodeIndexes) { + myMutableGraph = mutableGraph; + myBranchNodeIndexes = branchNodeIndexes; + } + + @Nullable + public GraphFragment getRelativeFragment(@NotNull GraphElement element) { + Node upNode; + int downVisibleIndex; + if (element instanceof Node) { + upNode = (Node) element; + downVisibleIndex = upNode.getVisibleNodeIndex(); + } else { + Edge edge = (Edge)element; + upNode = myMutableGraph.getNode(edge.getUpNodeVisibleIndex()); + downVisibleIndex = edge.getDownNodeVisibleIndex(); + } + + for (int i = 0; i < MAX_SEARCH_SIZE; i++) { + GraphFragment graphFragment = getDownFragment(upNode.getVisibleNodeIndex()); + if (graphFragment != null && graphFragment.downVisibleNodeIndex >= downVisibleIndex) + return graphFragment; + + if (upNode.getUpEdges().size() != 1) { + break; + } + upNode = myMutableGraph.getNode(upNode.getUpEdges().get(0).getUpNodeVisibleIndex()); + } + + return null; + } + + @Nullable + public GraphFragment getDownFragment(int upperVisibleNodeIndex) { + Pair fragment = getFragment(myMutableGraph.getIndexInPermanentGraph(upperVisibleNodeIndex), + downNodesFun, + upNodesFun, + thisNodeCantBeInMiddle); + return fragment == null ? null : new GraphFragment(myMutableGraph.toVisibleIndex(fragment.first), myMutableGraph.toVisibleIndex(fragment.second)); + } + + @Nullable + public GraphFragment getUpFragment(int lowerNodeIndex) { + Pair fragment = getFragment(myMutableGraph.getIndexInPermanentGraph(lowerNodeIndex), + upNodesFun, + downNodesFun, + thisNodeCantBeInMiddle); + return fragment == null ? null : new GraphFragment(myMutableGraph.toVisibleIndex(fragment.second), myMutableGraph.toVisibleIndex(fragment.first)); + } + + @Nullable + public GraphFragment getLongDownFragment(int rowIndex) { + return getLongFragment(getDownFragment(rowIndex), Integer.MAX_VALUE); + } + + @Nullable + public GraphFragment getLongFragment(@NotNull GraphElement element) { + return getLongFragment(getRelativeFragment(element), Integer.MAX_VALUE); + } + + // for hover + @Nullable + public GraphFragment getPartLongFragment(@NotNull GraphElement element) { + return getLongFragment(getRelativeFragment(element), 500); + } + + @Nullable + private GraphFragment getLongFragment(@Nullable GraphFragment startFragment, int bound) { + if (startFragment == null) + return null; + + GraphFragment shortFragment; + + int maxDown = startFragment.downVisibleNodeIndex; + while ((shortFragment = getDownFragment(maxDown)) != null && !myBranchNodeIndexes.contains(myMutableGraph.getIndexInPermanentGraph(maxDown))) { + maxDown = shortFragment.downVisibleNodeIndex; + if (maxDown - startFragment.downVisibleNodeIndex > bound) + break; + } + + int maxUp = startFragment.upVisibleNodeIndex; + while ((shortFragment = getUpFragment(maxUp)) != null && !myBranchNodeIndexes.contains(myMutableGraph.getIndexInPermanentGraph(maxUp))) { + maxUp = shortFragment.upVisibleNodeIndex; + if (startFragment.upVisibleNodeIndex - maxUp > bound) + break; + } + + if (maxUp != startFragment.upVisibleNodeIndex || maxDown != startFragment.downVisibleNodeIndex) { + return new GraphFragment(maxUp, maxDown); + } else { + if (myMutableGraph.getNode(startFragment.upVisibleNodeIndex).getDownEdges().size() != 1) + return startFragment; + } + return null; + } + + @Nullable + private static Pair getFragment(int startNode, + Function> getNextNodes, + Function> getPrevNodes, + Function thisNodeCantBeInMiddle) { + Set blackNodes = new HashSet(); + blackNodes.add(startNode); + + Set grayNodes = new HashSet(); + grayNodes.addAll(getNextNodes.fun(startNode)); + + int endNode = -1; + while (blackNodes.size() < SHORT_FRAGMENT_MAX_SIZE && !grayNodes.contains(SomeGraph.NOT_LOAD_COMMIT)) { + int nextBlackNode = -1; + for (int grayNode : grayNodes) { + if (blackNodes.containsAll(getPrevNodes.fun(grayNode))) { + nextBlackNode = grayNode; + break; + } + } + + if (nextBlackNode == -1) + return null; + + if (grayNodes.size() == 1) { + endNode = nextBlackNode; + break; + } + + List nextGrayNodes = getNextNodes.fun(nextBlackNode); + if (nextGrayNodes.isEmpty() || thisNodeCantBeInMiddle.fun(nextBlackNode)) + return null; + + blackNodes.add(nextBlackNode); + grayNodes.remove(nextBlackNode); + grayNodes.addAll(nextGrayNodes); + } + + if (endNode != -1) + return Pair.create(startNode, endNode); + else + return null; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/fragments/GraphFragment.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/fragments/GraphFragment.java new file mode 100644 index 000000000000..379d52be76f1 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/fragments/GraphFragment.java @@ -0,0 +1,27 @@ +/* + * 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.vcs.log.newgraph.gpaph.fragments; + +public class GraphFragment { + public final int upVisibleNodeIndex; + public final int downVisibleNodeIndex; + + public GraphFragment(int upVisibleNodeIndex, int downVisibleNodeIndex) { + this.upVisibleNodeIndex = upVisibleNodeIndex; + this.downVisibleNodeIndex = downVisibleNodeIndex; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/AbstractThickHoverController.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/AbstractThickHoverController.java new file mode 100644 index 000000000000..d098d9e88a98 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/AbstractThickHoverController.java @@ -0,0 +1,40 @@ +/* + * 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.vcs.log.newgraph.gpaph.impl; + +import com.intellij.vcs.log.newgraph.gpaph.ThickHoverController; +import com.intellij.vcs.log.newgraph.gpaph.actions.InternalGraphAction; +import com.intellij.vcs.log.newgraph.gpaph.actions.MouseOverArrowInternalGraphAction; +import com.intellij.vcs.log.newgraph.render.cell.SpecialRowElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class AbstractThickHoverController implements ThickHoverController { + @Nullable + private SpecialRowElement mySpecialRowElement = null; + + @Override + public boolean isHover(@NotNull SpecialRowElement specialRowElement) { + return specialRowElement.equals(mySpecialRowElement); + } + + public void performAction(@NotNull InternalGraphAction action) { + if (action instanceof MouseOverArrowInternalGraphAction) { + mySpecialRowElement = ((MouseOverArrowInternalGraphAction)action).getInfo(); + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/CollapsedMutableGraph.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/CollapsedMutableGraph.java new file mode 100644 index 000000000000..353d21fd1ad9 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/CollapsedMutableGraph.java @@ -0,0 +1,290 @@ +/* + * 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.vcs.log.newgraph.gpaph.impl; + +import com.intellij.openapi.util.Pair; +import com.intellij.util.BooleanFunction; +import com.intellij.util.SmartList; +import com.intellij.util.containers.hash.HashMap; +import com.intellij.vcs.log.newgraph.GraphFlags; +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.PermanentGraphLayout; +import com.intellij.vcs.log.newgraph.SomeGraph; +import com.intellij.vcs.log.newgraph.gpaph.*; +import com.intellij.vcs.log.newgraph.gpaph.actions.ClickInternalGraphAction; +import com.intellij.vcs.log.newgraph.gpaph.actions.InternalGraphAction; +import com.intellij.vcs.log.newgraph.gpaph.actions.LinearBranchesExpansionInternalGraphAction; +import com.intellij.vcs.log.newgraph.gpaph.fragments.FragmentGenerator; +import com.intellij.vcs.log.newgraph.gpaph.fragments.GraphFragment; +import com.intellij.vcs.log.newgraph.utils.DfsUtil; +import com.intellij.vcs.log.newgraph.utils.Flags; +import com.intellij.vcs.log.newgraph.utils.impl.TreeIntToIntMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.intellij.vcs.log.newgraph.utils.MyUtils.setAllValues; + +public class CollapsedMutableGraph extends MutableGraphWithHiddenNodes { + + public static CollapsedMutableGraph newInstance(@NotNull PermanentGraph permanentGraph, + @NotNull PermanentGraphLayout layout, + @NotNull GraphFlags graphFlags, + @NotNull Set branchNodeIndexes, + @NotNull DfsUtil dfsUtil) { + final Flags visibleNodes = graphFlags.getVisibleNodes(); + final Flags visibleNodesInBranches = graphFlags.getVisibleNodesInBranches(); + setAllValues(visibleNodes, true); + TreeIntToIntMap intToIntMap = TreeIntToIntMap.newInstance(new BooleanFunction() { + @Override + public boolean fun(Integer integer) { + return visibleNodes.get(integer) && visibleNodesInBranches.get(integer); + } + }, permanentGraph.nodesCount()); + + GraphWithElementsInfoImpl graphWithElementsInfo = + new GraphWithElementsInfoImpl(permanentGraph, visibleNodesInBranches, visibleNodes, intToIntMap, dfsUtil); + return new CollapsedMutableGraph(permanentGraph, layout, intToIntMap, graphFlags.getThickFlags(), + dfsUtil,branchNodeIndexes, graphWithElementsInfo); + } + + @NotNull + private final FragmentGenerator myFragmentGenerator; + + @NotNull + private final AbstractThickHoverController myThickHoverController; + + private CollapsedMutableGraph(@NotNull PermanentGraph permanentGraph, + @NotNull PermanentGraphLayout layout, + @NotNull TreeIntToIntMap intToIntMap, + @NotNull Flags thickFlags, + @NotNull DfsUtil dfsUtil, + @NotNull Set branchNodeIndexes, + @NotNull GraphWithElementsInfoImpl graphWithElementsInfo) { + super(intToIntMap, graphWithElementsInfo, layout); + myFragmentGenerator = new FragmentGenerator(this, branchNodeIndexes); + myThickHoverController = new ThickHoverControllerImpl(permanentGraph, this, myFragmentGenerator, thickFlags, dfsUtil); + } + + @Override + public int performAction(@NotNull InternalGraphAction action) { + myThickHoverController.performAction(action); + if (action instanceof ClickInternalGraphAction) { + GraphElement element = ((ClickInternalGraphAction)action).getInfo(); + if (element != null) { + Edge edge = containedCollapsedEdge(element); + if (edge != null) { + myGraph.expand(getIndexInPermanentGraph(edge.getUpNodeVisibleIndex()), + getIndexInPermanentGraph(edge.getDownNodeVisibleIndex())); + return edge.getUpNodeVisibleIndex(); + } + + GraphFragment fragment = myFragmentGenerator.getLongFragment(element); + if (fragment != null) { + myGraph.collapse(getIndexInPermanentGraph(fragment.upVisibleNodeIndex), + getIndexInPermanentGraph(fragment.downVisibleNodeIndex)); + return fragment.upVisibleNodeIndex; + } + } + } + + if (action instanceof LinearBranchesExpansionInternalGraphAction) { + Boolean info = ((LinearBranchesExpansionInternalGraphAction)action).getInfo(); + assert info != null; + boolean shouldExpand = info; + if (shouldExpand) + myGraph.expandAll(); + else { + int currentVisibleIndex = 0; + while (currentVisibleIndex < this.getCountVisibleNodes()) { + GraphFragment fragment = myFragmentGenerator.getLongDownFragment(currentVisibleIndex); + if (fragment != null) { + myGraph.collapse(getIndexInPermanentGraph(fragment.upVisibleNodeIndex), + getIndexInPermanentGraph(fragment.downVisibleNodeIndex)); + } + currentVisibleIndex++; + } + } + return 1; + } + + return -1; + } + + @NotNull + public GraphWithElementsInfo getInternalGraph() { + return myGraph; + } + + + @NotNull + @Override + public ThickHoverController getThickHoverController() { + return myThickHoverController; + } + + protected static class GraphWithElementsInfoImpl implements GraphWithElementsInfo { + @NotNull + private final PermanentGraph myPermanentGraph; + + @NotNull + private final Flags visibleNodesInBranches; + + @NotNull + private final Flags visibleNodes; + + @NotNull + private final TreeIntToIntMap intToIntMap; + + @NotNull + private final DfsUtil myDfsUtil; + + @NotNull + private final Map> upToEdge = new HashMap>(); + + @NotNull + private final Map> downToEdge = new HashMap>(); + + public GraphWithElementsInfoImpl(@NotNull PermanentGraph permanentGraph, + @NotNull Flags visibleNodesInBranches, + @NotNull Flags visibleNodes, + @NotNull TreeIntToIntMap intToIntMap, @NotNull DfsUtil dfsUtil) { + myPermanentGraph = permanentGraph; + this.visibleNodesInBranches = visibleNodesInBranches; + this.visibleNodes = visibleNodes; + this.intToIntMap = intToIntMap; + myDfsUtil = dfsUtil; + } + + // nodes up and down must be loaded + public void collapse(int upNodeIndex, final int downNodeIndex) { + Pair edge = Pair.create(upNodeIndex, downNodeIndex); + + myDfsUtil.nodeDfsIterator(upNodeIndex, new DfsUtil.NextNode() { + @Override + public int fun(int currentNode) { + if (upToEdge.containsKey(currentNode)) { + Pair edge = upToEdge.remove(currentNode); + downToEdge.remove(edge.second); + if (edge.second == downNodeIndex) + return NODE_NOT_FOUND; + + visibleNodes.set(edge.second, false); + return edge.second; + } + + for (int downNode : myPermanentGraph.getDownNodes(currentNode)) { + if (visibleNodes.get(downNode) && downNode != downNodeIndex) { + visibleNodes.set(downNode, false); + return downNode; + } + } + return NODE_NOT_FOUND; + } + }); + + upToEdge.put(upNodeIndex, edge); + downToEdge.put(downNodeIndex, edge); + intToIntMap.update(upNodeIndex, downNodeIndex); + } + + public void expand(int upNodeIndex, final int downNodeIndex) { + upToEdge.remove(upNodeIndex); + downToEdge.remove(downNodeIndex); + + myDfsUtil.nodeDfsIterator(upNodeIndex, new DfsUtil.NextNode() { + @Override + public int fun(int currentNode) { + for (int downNode : myPermanentGraph.getDownNodes(currentNode)) { + if (!visibleNodes.get(downNode) && downNode != downNodeIndex) { + visibleNodes.set(downNode, true); + return downNode; + } + } + return NODE_NOT_FOUND; + } + }); + intToIntMap.update(upNodeIndex, downNodeIndex); + } + + public boolean nodeIsVisible(int nodeIndex) { + if (nodeIndex == SomeGraph.NOT_LOAD_COMMIT) + return true; + return visibleNodes.get(nodeIndex) && visibleNodesInBranches.get(nodeIndex); + } + + public void expandAll() { + upToEdge.clear(); + downToEdge.clear(); + setAllValues(visibleNodes, true); + intToIntMap.update(0, myPermanentGraph.nodesCount() - 1); + } + + @NotNull + @Override + public Node.Type getNodeType(int nodeIndex) { + return Node.Type.USUAL; + } + + @NotNull + @Override + public Edge.Type getEdgeType(int upNodeIndex, int downNodeIndex) { + if (upToEdge.containsKey(upNodeIndex)) + return Edge.Type.HIDE_FRAGMENT; + else + return Edge.Type.USUAL; + } + + @Override + public int nodesCount() { + return intToIntMap.shortSize(); + } + + @NotNull + @Override + public List getUpNodes(int nodeIndex) { + Pair edge = downToEdge.get(nodeIndex); + if (edge != null) + return Collections.singletonList(edge.first); + + List upNodes = new SmartList(); + for (int upNode : myPermanentGraph.getUpNodes(nodeIndex)) { + if (nodeIsVisible(upNode)) + upNodes.add(upNode); + } + return upNodes; + } + + @NotNull + @Override + public List getDownNodes(int nodeIndex) { + Pair edge = upToEdge.get(nodeIndex); + if (edge != null) + return Collections.singletonList(edge.second); + + List downNodes = new SmartList(); + for (int downNode : myPermanentGraph.getDownNodes(nodeIndex)) { + if (nodeIsVisible(downNode)) + downNodes.add(downNode); + } + return downNodes; + + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/EdgeImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/EdgeImpl.java new file mode 100644 index 000000000000..b5e8ee87c609 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/EdgeImpl.java @@ -0,0 +1,81 @@ +/* + * 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.vcs.log.newgraph.gpaph.impl; + +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import org.jetbrains.annotations.NotNull; + +public class EdgeImpl implements Edge { + private final int myUpVisibleIndex; + private final int myDownVisibleIndex; + + @NotNull + private final Type myType; + + private final int myLayoutIndex; + + public EdgeImpl(int upVisibleIndex, int downVisibleIndex, @NotNull Type type, int layoutIndex) { + myUpVisibleIndex = upVisibleIndex; + myDownVisibleIndex = downVisibleIndex; + myType = type; + myLayoutIndex = layoutIndex; + } + + @Override + public int getUpNodeVisibleIndex() { + return myUpVisibleIndex; + } + + @Override + public int getDownNodeVisibleIndex() { + return myDownVisibleIndex; + } + + @NotNull + @Override + public Type getType() { + return myType; + } + + @Override + public int getLayoutIndex() { + return myLayoutIndex; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EdgeImpl)) return false; + + EdgeImpl edge = (EdgeImpl)o; + + if (myDownVisibleIndex != edge.myDownVisibleIndex) return false; + if (myLayoutIndex != edge.myLayoutIndex) return false; + if (myUpVisibleIndex != edge.myUpVisibleIndex) return false; + if (myType != edge.myType) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myUpVisibleIndex; + result = 31 * result + myDownVisibleIndex; + result = 31 * result + myType.hashCode(); + result = 31 * result + myLayoutIndex; + return result; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/FilterMutableGraph.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/FilterMutableGraph.java new file mode 100644 index 000000000000..63239a7b09a2 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/FilterMutableGraph.java @@ -0,0 +1,181 @@ +/* + * 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.vcs.log.newgraph.gpaph.impl; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Condition; +import com.intellij.util.BooleanFunction; +import com.intellij.util.SmartList; +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.PermanentGraphLayout; +import com.intellij.vcs.log.newgraph.SomeGraph; +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import com.intellij.vcs.log.newgraph.gpaph.GraphWithElementsInfo; +import com.intellij.vcs.log.newgraph.gpaph.Node; +import com.intellij.vcs.log.newgraph.gpaph.ThickHoverController; +import com.intellij.vcs.log.newgraph.gpaph.actions.InternalGraphAction; +import com.intellij.vcs.log.newgraph.utils.Flags; +import com.intellij.vcs.log.newgraph.utils.IntToIntMap; +import com.intellij.vcs.log.newgraph.utils.impl.TreeIntToIntMap; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class FilterMutableGraph extends MutableGraphWithHiddenNodes { + private static final Logger LOG = Logger.getInstance(FilterMutableGraph.class); + + public static FilterMutableGraph newInstance(@NotNull PermanentGraph permanentGraph, + @NotNull PermanentGraphLayout layout, + @NotNull final Flags visibleNodesInBranches, + @NotNull final Flags visibleNodes, + @NotNull Condition isVisibleNode) { + + for (int i = 0; i < permanentGraph.nodesCount(); i++) { + if (isVisibleNode.value(i) && !visibleNodesInBranches.get(i)) + LOG.debug("Filter give me commit, which hidden in current branches visibility; commitHashIndex: " + permanentGraph.getHashIndex(i)); + + visibleNodes.set(i, isVisibleNode.value(i)); + } + TreeIntToIntMap visibleToReal = TreeIntToIntMap.newInstance(new BooleanFunction() { + @Override + public boolean fun(Integer integer) { + return visibleNodes.get(integer); + } + }, permanentGraph.nodesCount()); + + return new FilterMutableGraph(permanentGraph, layout, isVisibleNode, visibleToReal, visibleNodes); + } + + @NotNull + private final Flags myVisibleNodes; + + @NotNull + private final PermanentGraph myPermanentGraph; + + @NotNull + private final Condition isVisibleNode; + + @NotNull + private final FilterThickHoverController myThickHoverController; + + @NotNull + private final TreeIntToIntMap myTreeVisibleToReal; + + + private FilterMutableGraph(@NotNull PermanentGraph permanentGraph, + @NotNull PermanentGraphLayout layout, + @NotNull Condition isVisibleNode, + @NotNull TreeIntToIntMap visibleToReal, + @NotNull Flags visibleNodes) { + super(visibleToReal, new GraphWithElementsInfoImpl(permanentGraph, visibleNodes, visibleToReal), layout); + myTreeVisibleToReal = visibleToReal; + myVisibleNodes = visibleNodes; + myPermanentGraph = permanentGraph; + this.isVisibleNode = isVisibleNode; + myThickHoverController = new FilterThickHoverController(); + } + + + @Override + public int performAction(@NotNull InternalGraphAction action) { + myThickHoverController.performAction(action); + + return -1; + } + + public boolean nextRowIsHide(int visibleRowIndex) { + int realIndex = myVisibleToReal.getLongIndex(visibleRowIndex); + return realIndex < myPermanentGraph.nodesCount() - 1 && ! myVisibleNodes.get(realIndex + 1); + } + + // for future + @SuppressWarnings("unused") + private void showHideFragment(int visibleRowIndex) { + int startIndex = myVisibleToReal.getLongIndex(visibleRowIndex); + int endIndex; + for (endIndex = startIndex + 1; endIndex < myPermanentGraph.nodesCount(); endIndex++) { + if (myVisibleNodes.get(endIndex)) { + break; + } else { + myVisibleNodes.set(endIndex, true); + } + } + if (endIndex == myPermanentGraph.nodesCount()) + endIndex--; + + myTreeVisibleToReal.update(startIndex, endIndex); + } + + @NotNull + @Override + public ThickHoverController getThickHoverController() { + return myThickHoverController; + } + + protected static class GraphWithElementsInfoImpl implements GraphWithElementsInfo { + private final PermanentGraph myGraph; + private final Flags myVisibleNodes; + private final IntToIntMap myIntToIntMap; + + private GraphWithElementsInfoImpl(PermanentGraph graph, Flags visibleNodes, IntToIntMap intToIntMap) { + myGraph = graph; + myVisibleNodes = visibleNodes; + myIntToIntMap = intToIntMap; + } + + @NotNull + @Override + public Node.Type getNodeType(int nodeIndex) { + return Node.Type.USUAL; + } + + @NotNull + @Override + public Edge.Type getEdgeType(int upNodeIndex, int downNodeIndex) { + return Edge.Type.USUAL; + } + + @Override + public int nodesCount() { + return myIntToIntMap.shortSize(); + } + + @NotNull + @Override + public List getUpNodes(int nodeIndex) { + List result = new SmartList(); + for (int upNode : myGraph.getUpNodes(nodeIndex)) { + if (myVisibleNodes.get(upNode)) + result.add(upNode); + } + + return result; + } + + @NotNull + @Override + public List getDownNodes(int nodeIndex) { + List result = new SmartList(); + for (int downNode : myGraph.getDownNodes(nodeIndex)) { + if (downNode != SomeGraph.NOT_LOAD_COMMIT && myVisibleNodes.get(downNode)) + result.add(downNode); + } + + return result; + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/FilterThickHoverController.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/FilterThickHoverController.java new file mode 100644 index 000000000000..f33d1e84fc39 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/FilterThickHoverController.java @@ -0,0 +1,33 @@ +/* + * 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.vcs.log.newgraph.gpaph.impl; + +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import org.jetbrains.annotations.NotNull; + +public class FilterThickHoverController extends AbstractThickHoverController { + @Override + public boolean isThick(@NotNull GraphElement element) { + return false; + } + + @Override + public boolean isHover(@NotNull GraphElement element) { + return false; + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/MutableGraphWithHiddenNodes.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/MutableGraphWithHiddenNodes.java new file mode 100644 index 000000000000..c572d59c2e45 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/MutableGraphWithHiddenNodes.java @@ -0,0 +1,125 @@ +/* + * 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.vcs.log.newgraph.gpaph.impl; + +import com.intellij.util.SmartList; +import com.intellij.vcs.log.newgraph.PermanentGraphLayout; +import com.intellij.vcs.log.newgraph.SomeGraph; +import com.intellij.vcs.log.newgraph.gpaph.*; +import com.intellij.vcs.log.newgraph.utils.IntToIntMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public abstract class MutableGraphWithHiddenNodes implements MutableGraph { + + @Nullable + public static Edge containedCollapsedEdge(@NotNull GraphElement element) { + if (element instanceof Edge) { + Edge edge = (Edge)element; + if (edge.getType() == Edge.Type.HIDE_FRAGMENT) + return edge; + + } else { + Node node = (Node)element; + for (Edge upEdge : node.getUpEdges()) + if (upEdge.getType() == Edge.Type.HIDE_FRAGMENT) return upEdge; + + for (Edge downEdge : node.getDownEdges()) + if (downEdge.getType() == Edge.Type.HIDE_FRAGMENT) return downEdge; + } + + return null; + } + + @NotNull + protected final IntToIntMap myVisibleToReal; + + @NotNull + protected final T myGraph; + + @NotNull + protected final PermanentGraphLayout myLayout; + + protected MutableGraphWithHiddenNodes(@NotNull IntToIntMap visibleToReal, @NotNull T graph, @NotNull PermanentGraphLayout layout) { + myVisibleToReal = visibleToReal; + myGraph = graph; + myLayout = layout; + } + + @Override + public int getCountVisibleNodes() { + return myVisibleToReal.shortSize(); + } + + @NotNull + @Override + public Node getNode(int visibleNodeIndex) { + int nodeIndex = toNodeIndex(visibleNodeIndex); + List upEdges = convertToEdgeList(myGraph.getUpNodes(nodeIndex), nodeIndex, /*toUp = */ true); + List downEdges = convertToEdgeList(myGraph.getDownNodes(nodeIndex), nodeIndex, /*toUp = */ false); + + Node.Type nodeType = myGraph.getNodeType(nodeIndex); + int nodeLayoutIndex = myLayout.getLayoutIndex(nodeIndex); + return new NodeImpl(visibleNodeIndex, nodeType, upEdges, downEdges, nodeLayoutIndex); + } + + @Override + public int getIndexInPermanentGraph(int visibleNodeIndex) { + return toNodeIndex(visibleNodeIndex); + } + + public int toVisibleIndex(int nodeIndexInPermanentGraph) { + if (nodeIndexInPermanentGraph == SomeGraph.NOT_LOAD_COMMIT) + return SomeGraph.NOT_LOAD_COMMIT; + return myVisibleToReal.getShortIndex(nodeIndexInPermanentGraph); + } + + protected int toNodeIndex(int visibleNodeIndex) { + if (visibleNodeIndex == SomeGraph.NOT_LOAD_COMMIT) + return SomeGraph.NOT_LOAD_COMMIT; + return myVisibleToReal.getLongIndex(visibleNodeIndex); + } + + @NotNull + private List convertToEdgeList(@NotNull List destinationNodeIndexes, int startNodeIndex, boolean toUp) { + List result = new SmartList(); + for (int destinationIndex : destinationNodeIndexes) { + int upNodeIndex, downNodeIndex; + if (toUp) { + upNodeIndex = destinationIndex; + downNodeIndex = startNodeIndex; + } else { + downNodeIndex = destinationIndex; + upNodeIndex = startNodeIndex; + } + Edge.Type type = myGraph.getEdgeType(upNodeIndex, downNodeIndex); + int layoutIndex = getEdgeLayoutIndex(upNodeIndex, downNodeIndex); + result.add(new EdgeImpl(toVisibleIndex(upNodeIndex), toVisibleIndex(downNodeIndex), type, layoutIndex)); + } + return result; + } + + private int getEdgeLayoutIndex(int upNodeIndex, int downNodeIndex) { + if (downNodeIndex == SomeGraph.NOT_LOAD_COMMIT) { + return myLayout.getLayoutIndex(upNodeIndex); // i.e. edge to not load commit + } + int upNodeLayoutIndex = myLayout.getLayoutIndex(upNodeIndex); + int downNodeLayoutIndex = myLayout.getLayoutIndex(downNodeIndex); + return Math.max(upNodeLayoutIndex, downNodeLayoutIndex); + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/NodeImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/NodeImpl.java new file mode 100644 index 000000000000..bc0ae26003f5 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/NodeImpl.java @@ -0,0 +1,95 @@ +/* + * 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.vcs.log.newgraph.gpaph.impl; + +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import com.intellij.vcs.log.newgraph.gpaph.Node; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class NodeImpl implements Node { + private final int myVisibleNodeIndex; + + @NotNull + private final Type myType; + + @NotNull + private final List myUpEdges; + + @NotNull + private final List myDownEdges; + private final int myLayoutIndex; + + public NodeImpl(int visibleNodeIndex, @NotNull Type type, @NotNull List upEdges, @NotNull List downEdges, int layoutIndex) { + myVisibleNodeIndex = visibleNodeIndex; + myType = type; + myUpEdges = upEdges; + myDownEdges = downEdges; + myLayoutIndex = layoutIndex; + } + + + @Override + public int getVisibleNodeIndex() { + return myVisibleNodeIndex; + } + + @NotNull + @Override + public Type getType() { + return myType; + } + + @NotNull + @Override + public List getUpEdges() { + return myUpEdges; + } + + @NotNull + @Override + public List getDownEdges() { + return myDownEdges; + } + + @Override + public int getLayoutIndex() { + return myLayoutIndex; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NodeImpl)) return false; + + NodeImpl node = (NodeImpl)o; + + if (myLayoutIndex != node.myLayoutIndex) return false; + if (myVisibleNodeIndex != node.myVisibleNodeIndex) return false; + if (myType != node.myType) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myVisibleNodeIndex; + result = 31 * result + myType.hashCode(); + result = 31 * result + myLayoutIndex; + return result; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/PermanentAsMutableGraph.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/PermanentAsMutableGraph.java new file mode 100644 index 000000000000..493258c3f9ce --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/PermanentAsMutableGraph.java @@ -0,0 +1,89 @@ +/* + * 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.vcs.log.newgraph.gpaph.impl; + +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.PermanentGraphLayout; +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import com.intellij.vcs.log.newgraph.gpaph.GraphWithElementsInfo; +import com.intellij.vcs.log.newgraph.gpaph.Node; +import com.intellij.vcs.log.newgraph.gpaph.ThickHoverController; +import com.intellij.vcs.log.newgraph.gpaph.actions.InternalGraphAction; +import com.intellij.vcs.log.newgraph.utils.impl.IDIntToInt; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class PermanentAsMutableGraph extends MutableGraphWithHiddenNodes { + + @NotNull + private final ThickHoverController myThickHoverController; + + public PermanentAsMutableGraph(@NotNull PermanentGraph graph, + @NotNull PermanentGraphLayout layout, + @NotNull ThickHoverController thickHoverController) { + super(new IDIntToInt(graph.nodesCount()), new GraphWithElementsInfoImpl(graph), layout); + myThickHoverController = thickHoverController; + } + + @Override + public int performAction(@NotNull InternalGraphAction action) { + return -1; + } + + @NotNull + @Override + public ThickHoverController getThickHoverController() { + return myThickHoverController; + } + + protected static class GraphWithElementsInfoImpl implements GraphWithElementsInfo { + private final PermanentGraph myGraph; + + private GraphWithElementsInfoImpl(PermanentGraph graph) { + myGraph = graph; + } + + @NotNull + @Override + public Node.Type getNodeType(int nodeIndex) { + return Node.Type.USUAL; + } + + @NotNull + @Override + public Edge.Type getEdgeType(int upNodeIndex, int downNodeIndex) { + return Edge.Type.USUAL; + } + + @Override + public int nodesCount() { + return myGraph.nodesCount(); + } + + @NotNull + @Override + public List getUpNodes(int nodeIndex) { + return myGraph.getUpNodes(nodeIndex); + } + + @NotNull + @Override + public List getDownNodes(int nodeIndex) { + return myGraph.getDownNodes(nodeIndex); + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/ThickHoverControllerImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/ThickHoverControllerImpl.java new file mode 100644 index 000000000000..2bdafc7d874e --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/ThickHoverControllerImpl.java @@ -0,0 +1,187 @@ +/* + * 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.vcs.log.newgraph.gpaph.impl; + +import com.intellij.util.Function; +import com.intellij.util.containers.HashSet; +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.SomeGraph; +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import com.intellij.vcs.log.newgraph.gpaph.MutableGraph; +import com.intellij.vcs.log.newgraph.gpaph.Node; +import com.intellij.vcs.log.newgraph.gpaph.actions.InternalGraphAction; +import com.intellij.vcs.log.newgraph.gpaph.actions.MouseOverGraphElementInternalGraphAction; +import com.intellij.vcs.log.newgraph.gpaph.actions.SelectAllRelativeCommitsInternalGraphAction; +import com.intellij.vcs.log.newgraph.gpaph.fragments.FragmentGenerator; +import com.intellij.vcs.log.newgraph.gpaph.fragments.GraphFragment; +import com.intellij.vcs.log.newgraph.utils.DfsUtil; +import com.intellij.vcs.log.newgraph.utils.Flags; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +import static com.intellij.vcs.log.newgraph.utils.MyUtils.setAllValues; + +public class ThickHoverControllerImpl extends AbstractThickHoverController { + @NotNull + private final PermanentGraph myPermanentGraph; + + @NotNull + private final MutableGraph myMutableGraph; + + @NotNull + private final FragmentGenerator myFragmentGenerator; + + @NotNull + private final Set hoverNodes = new HashSet(); // contains visibleNodeIndex + + @NotNull + private final Flags myThickFlags; + + @NotNull + private final DfsUtil myDfsUtil; + + public ThickHoverControllerImpl(@NotNull PermanentGraph permanentGraph, + @NotNull MutableGraph mutableGraph, + @NotNull FragmentGenerator fragmentGenerator, + @NotNull Flags thickFlags, + @NotNull DfsUtil dfsUtil) { + myPermanentGraph = permanentGraph; + myMutableGraph = mutableGraph; + myFragmentGenerator = fragmentGenerator; + myThickFlags = thickFlags; + myDfsUtil = dfsUtil; + } + + @Override + public boolean isThick(@NotNull GraphElement element) { + return isOn(element, new Function() { + @Override + public Boolean fun(Integer integer) { + return myThickFlags.get(myMutableGraph.getIndexInPermanentGraph(integer)); + } + }); + } + + @Override + public boolean isHover(@NotNull GraphElement element) { + return isOn(element, new Function() { + @Override + public Boolean fun(Integer integer) { + return hoverNodes.contains(integer); + } + }); + } + + private static boolean isOn(@NotNull GraphElement element, @NotNull Function visibleNodeIndexToOn) { + if (element instanceof Node) + return visibleNodeIndexToOn.fun(((Node)element).getVisibleNodeIndex()); + + if (element instanceof Edge) { + Edge edge = (Edge) element; + int visibleUpNodeIndex = edge.getUpNodeVisibleIndex(); + int visibleDownNodeIndex = edge.getDownNodeVisibleIndex(); + + if (visibleDownNodeIndex == SomeGraph.NOT_LOAD_COMMIT) + return false; + else + return visibleNodeIndexToOn.fun(visibleUpNodeIndex) && visibleNodeIndexToOn.fun(visibleDownNodeIndex); + } + + return false; + } + + @Override + public void performAction(@NotNull InternalGraphAction action) { + super.performAction(action); + if (action instanceof SelectAllRelativeCommitsInternalGraphAction) { + setAllValues(myThickFlags, false); + Integer visibleNodeIndex = ((SelectAllRelativeCommitsInternalGraphAction)action).getInfo(); + if (visibleNodeIndex != null) { + int realRowIndex = myMutableGraph.getIndexInPermanentGraph(visibleNodeIndex); + enableAllRelativeNodes(myThickFlags, realRowIndex); + } + } + + if (action instanceof MouseOverGraphElementInternalGraphAction) { + hoverNodes.clear(); + GraphElement graphElement = ((MouseOverGraphElementInternalGraphAction)action).getInfo(); + if (graphElement != null) + hoverFragment(graphElement); + } + } + + private void enableAllRelativeNodes(@NotNull final Flags flags, final int rowIndex) { + flags.set(rowIndex, true); + + myDfsUtil.nodeDfsIterator(rowIndex, new DfsUtil.NextNode() { + @Override + public int fun(int currentNode) { + for (int downNode : myPermanentGraph.getDownNodes(currentNode)) { + if (downNode != SomeGraph.NOT_LOAD_COMMIT && !flags.get(downNode)) { + flags.set(downNode, true); + return downNode; + } + } + return DfsUtil.NextNode.NODE_NOT_FOUND; + } + }); + + myDfsUtil.nodeDfsIterator(rowIndex, new DfsUtil.NextNode() { + @Override + public int fun(int currentNode) { + for (int upNode : myPermanentGraph.getUpNodes(currentNode)) { + if (!flags.get(upNode)) { + flags.set(upNode, true); + return upNode; + } + } + return DfsUtil.NextNode.NODE_NOT_FOUND; + } + }); + } + + private void hoverFragment(@NotNull GraphElement graphElement) { + Edge collapsedEdge = MutableGraphWithHiddenNodes.containedCollapsedEdge(graphElement); + if (collapsedEdge != null) { + hoverNodes.add(collapsedEdge.getUpNodeVisibleIndex()); + hoverNodes.add(collapsedEdge.getDownNodeVisibleIndex()); + return; + } + + GraphFragment fragment = myFragmentGenerator.getPartLongFragment(graphElement); + if (fragment == null) + return; + + hoverNodes.add(fragment.upVisibleNodeIndex); + hoverNodes.add(fragment.downVisibleNodeIndex); + + myDfsUtil.nodeDfsIterator(fragment.upVisibleNodeIndex, new DfsUtil.NextNode() { + @Override + public int fun(int currentNode) { + for (Edge downEdge : myMutableGraph.getNode(currentNode).getDownEdges()) { + int downNode = downEdge.getDownNodeVisibleIndex(); + if (hoverNodes.add(downNode)) + return downNode; + } + return DfsUtil.NextNode.NODE_NOT_FOUND; + } + }); + + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/ThickHoverControllerTest.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/ThickHoverControllerTest.java new file mode 100644 index 000000000000..430706eaa181 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/gpaph/impl/ThickHoverControllerTest.java @@ -0,0 +1,48 @@ +/* + * 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.vcs.log.newgraph.gpaph.impl; + +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import com.intellij.vcs.log.newgraph.gpaph.actions.InternalGraphAction; +import com.intellij.vcs.log.newgraph.gpaph.actions.MouseOverGraphElementInternalGraphAction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ThickHoverControllerTest extends AbstractThickHoverController { + + @Nullable + private GraphElement hoverElement = null; + + + @Override + public boolean isThick(@NotNull GraphElement element) { + return false; + } + + @Override + public boolean isHover(@NotNull GraphElement element) { + return element.equals(hoverElement); + } + + @Override + public void performAction(@NotNull InternalGraphAction action) { + super.performAction(action); + if (action instanceof MouseOverGraphElementInternalGraphAction) { + hoverElement = ((MouseOverGraphElementInternalGraphAction)action).getInfo(); + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/DuplicateParentFixer.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/DuplicateParentFixer.java new file mode 100644 index 000000000000..d59532f97d1f --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/DuplicateParentFixer.java @@ -0,0 +1,81 @@ +/* + * 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.vcs.log.newgraph.impl; + +import com.intellij.vcs.log.GraphCommit; + +import java.util.AbstractList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class DuplicateParentFixer { + + public static AbstractList fixDuplicateParentCommits(final List finalCommits) { + return new AbstractList() { + @Override + public GraphCommit get(int index) { + final GraphCommit commit = finalCommits.get(index); + return new GraphCommit() { + @Override + public int getIndex() { + return commit.getIndex(); + } + + @Override + public int[] getParentIndices() { + return fixParentsDuplicate(commit.getParentIndices()); + } + }; + } + + @Override + public int size() { + return finalCommits.size(); + } + }; + } + + private static int[] fixParentsDuplicate(int[] commitParents) { + if (commitParents.length == 1) + return commitParents; + + if (commitParents.length == 2) { + if (commitParents[0] != commitParents[1]) + return commitParents; + else + return new int[]{commitParents[0]}; + } + + Set hashes = new HashSet(); + for (int hashIndex : commitParents) + hashes.add(hashIndex); + + if (hashes.size() == commitParents.length) + return commitParents; + + int[] newCommitParents = new int[hashes.size()]; + int offset = 0; + for (int hashIndex : commitParents) { + if (hashes.remove(hashIndex)) { + newCommitParents[offset] = hashIndex; + offset++; + } + } + return newCommitParents; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/PermanentGraphBuilder.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/PermanentGraphBuilder.java new file mode 100644 index 000000000000..24356316ddff --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/PermanentGraphBuilder.java @@ -0,0 +1,184 @@ +/* + * 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.vcs.log.newgraph.impl; + +import com.intellij.openapi.util.Pair; +import com.intellij.util.SmartList; +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.newgraph.SomeGraph; +import com.intellij.vcs.log.newgraph.utils.Flags; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +import static com.intellij.vcs.log.newgraph.impl.DuplicateParentFixer.fixDuplicateParentCommits; + +public class PermanentGraphBuilder { + + @NotNull + public static Pair> build(@NotNull Flags simpleNodes, @NotNull List commits) { + commits = fixDuplicateParentCommits(commits); + assert commits.size() == simpleNodes.size(); + + int longEdgesCount = 0; + int[] nodeToHashIndex = new int[commits.size()]; + + for (int nodeIndex = 0; nodeIndex < commits.size(); nodeIndex++) { + GraphCommit commit = commits.get(nodeIndex); + nodeToHashIndex[nodeIndex] = commit.getIndex(); + + int nextCommitHashIndex = nextCommitHashIndex(commits, nodeIndex); + + int[] parentHashIndices = commit.getParentIndices(); + if (parentHashIndices.length == 1 && parentHashIndices[0] == nextCommitHashIndex) { + simpleNodes.set(nodeIndex, true); + } else { + longEdgesCount += parentHashIndices.length; + } + } + + PermanentGraphBuilder builder = new PermanentGraphBuilder(commits, simpleNodes, longEdgesCount, nodeToHashIndex); + PermanentGraphImpl permanentGraph = builder.build(); + return new Pair>(permanentGraph, builder.commitsWithNotLoadParentMap); + } + + private static int nextCommitHashIndex(List commits, int nodeIndex) { + int nextCommitHashIndex = -1; + if (nodeIndex < commits.size() - 1) + nextCommitHashIndex = commits.get(nodeIndex + 1).getIndex(); + return nextCommitHashIndex; + } + + + private final List myCommits; + private final Flags mySimpleNodes; + + private final int myNodesCount; + + private final int[] myNodeToHashIndex; + + private final int[] myNodeToEdgeIndex; + private final int[] myLongEdges; + + // downHashIndex -> List of upNodeIndex + private final Map> upAdjacentNodes = new HashMap>(); + + // commitHash -> GraphCommit + private final Map commitsWithNotLoadParentMap = new HashMap(); + + private PermanentGraphBuilder(List commits, Flags simpleNodes, int longEdgesCount, int[] nodeToHashIndex) { + myCommits = commits; + mySimpleNodes = simpleNodes; + myNodeToHashIndex = nodeToHashIndex; + + myNodesCount = simpleNodes.size(); + + myNodeToEdgeIndex = new int[myNodesCount + 1]; + myLongEdges = new int[2 * longEdgesCount]; + } + + private void addUnderdoneEdge(int upNodeIndex, int downHashIndex) { + List upNodes = upAdjacentNodes.get(downHashIndex); + if (upNodes == null) { + upNodes = new SmartList(); + upAdjacentNodes.put(downHashIndex, upNodes); + } + upNodes.add(upNodeIndex); + } + + private void addCommitWithNotLoadParent(int nodeIndex) { + GraphCommit commit = myCommits.get(nodeIndex); + commitsWithNotLoadParentMap.put(commit.getIndex(), commit); + } + + private void fixUnderdoneEdgeForNotLoadCommit(int upNodeIndex) { + for (int edgeIndex = myNodeToEdgeIndex[upNodeIndex]; edgeIndex < myNodeToEdgeIndex[upNodeIndex + 1]; edgeIndex++) { + if (myLongEdges[edgeIndex] == -1) { + addCommitWithNotLoadParent(upNodeIndex); + myLongEdges[edgeIndex] = SomeGraph.NOT_LOAD_COMMIT; + return; + } + } + throw new IllegalStateException("Not found underdone edge to not load commit for node: " + upNodeIndex); + } + + private void fixUnderdoneEdge(int upNodeIndex, int downNodeIndex, int downNodeHashIndex) { + int end = myNodeToEdgeIndex[upNodeIndex + 1]; + + GraphCommit upCommit = myCommits.get(upNodeIndex); + int[] parentHashIndices = upCommit.getParentIndices(); + + for (int i = 0; i < parentHashIndices.length; i++) { + if (parentHashIndices[i] == downNodeHashIndex) { + int offset = parentHashIndices.length - i; + int edgeIndex = end - offset; + + if (myLongEdges[edgeIndex] == -1) { + myLongEdges[edgeIndex] = downNodeIndex; + return; + } else { + throw new IllegalStateException("Edge was set early!. Up node: " + upNodeIndex + ", down node: " + downNodeIndex); + } + } + } + throw new IllegalStateException("Not found underdone edges for node: " + upNodeIndex + ". Adjacent down node: " + downNodeIndex); + } + + private void doStep(int nodeIndex) { + GraphCommit commit = myCommits.get(nodeIndex); + + List upNodes = upAdjacentNodes.remove(commit.getIndex()); + if (upNodes == null) + upNodes = Collections.emptyList(); + + int edgeIndex = myNodeToEdgeIndex[nodeIndex]; + for (Integer upNodeIndex : upNodes) { + fixUnderdoneEdge(upNodeIndex, nodeIndex, commit.getIndex()); + myLongEdges[edgeIndex] = upNodeIndex; + edgeIndex++; + } + + // down nodes + if (!mySimpleNodes.get(nodeIndex)) { + for (Integer downHashIndex : commit.getParentIndices()) { + addUnderdoneEdge(nodeIndex, downHashIndex); + myLongEdges[edgeIndex] = -1; + edgeIndex++; + } + } + + myNodeToEdgeIndex[nodeIndex + 1] = edgeIndex; + } + + private void fixUnderdoneEdges() { + for (List upNodes : upAdjacentNodes.values()) { + for (Integer upNodeIndex : upNodes) + fixUnderdoneEdgeForNotLoadCommit(upNodeIndex); + } + } + + private PermanentGraphImpl build() { + for (int nodeIndex = 0; nodeIndex < myNodesCount; nodeIndex++) { + doStep(nodeIndex); + } + + fixUnderdoneEdges(); + + return new PermanentGraphImpl(mySimpleNodes, myNodeToHashIndex, myNodeToEdgeIndex, myLongEdges); + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/PermanentGraphImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/PermanentGraphImpl.java new file mode 100644 index 000000000000..503def3dcaf4 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/PermanentGraphImpl.java @@ -0,0 +1,104 @@ +/* + * 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.vcs.log.newgraph.impl; + +import com.intellij.util.SmartList; +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.utils.Flags; +import org.jetbrains.annotations.NotNull; + +import java.util.AbstractList; +import java.util.Collections; +import java.util.List; + +public class PermanentGraphImpl implements PermanentGraph { + private final Flags mySimpleNodes; + private final int[] myNodeToHashIndex; + + // myNodeToEdgeIndex.length = nodesCount() + 1. See adjacentNodes(). + private final int[] myNodeToEdgeIndex; + private final int[] myLongEdges; + + /*package*/ PermanentGraphImpl(Flags simpleNodes, int[] nodeToHashIndex, int[] nodeToEdgeIndex, int[] longEdges) { + mySimpleNodes = simpleNodes; + myNodeToHashIndex = nodeToHashIndex; + myNodeToEdgeIndex = nodeToEdgeIndex; + myLongEdges = longEdges; + } + + @Override + public int nodesCount() { + return mySimpleNodes.size(); + } + + @NotNull + private List adjacentNodes(final int nodeIndex) { + final int startIndex = myNodeToEdgeIndex[nodeIndex]; + + return new AbstractList() { + @Override + public Integer get(int index) { + return myLongEdges[startIndex + index]; + } + + @Override + public int size() { + return myNodeToEdgeIndex[nodeIndex + 1] - startIndex; + } + }; + } + + @NotNull + @Override + public List getUpNodes(int nodeIndex) { + List result = new SmartList(); + if (nodeIndex != 0 && mySimpleNodes.get(nodeIndex - 1)) { + result.add(nodeIndex - 1); + } + + for (Integer node: adjacentNodes(nodeIndex)) { + if (node < nodeIndex) + result.add(node); + } + + return result; + } + + + @NotNull + @Override + public List getDownNodes(int nodeIndex) { + if (mySimpleNodes.get(nodeIndex)) { + return Collections.singletonList(nodeIndex + 1); + } + + List result = new SmartList(); + for (Integer node: adjacentNodes(nodeIndex)) { + if (nodeIndex < node) + result.add(node); + } + + return result; + } + + @Override + public int getHashIndex(int nodeIndex) { + if (nodeIndex == nodesCount()) + return NOT_LOAD_COMMIT; + return myNodeToHashIndex[nodeIndex]; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/PermanentGraphLayoutBuilder.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/PermanentGraphLayoutBuilder.java new file mode 100644 index 000000000000..178264a92ef6 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/impl/PermanentGraphLayoutBuilder.java @@ -0,0 +1,155 @@ +/* + * 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.vcs.log.newgraph.impl; + +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.PermanentGraphLayout; +import com.intellij.vcs.log.newgraph.SomeGraph; +import com.intellij.vcs.log.newgraph.utils.DfsUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class PermanentGraphLayoutBuilder { + + @NotNull + public static PermanentGraphLayout build(@NotNull DfsUtil dfsUtil, + @NotNull PermanentGraph graph, + @NotNull Comparator compareTwoHeaderNodeIndex) { + List heads = new ArrayList(); + for (int i = 0; i < graph.nodesCount(); i++) { + if (graph.getUpNodes(i).size() == 0) { + heads.add(i); + } + } + Collections.sort(heads, compareTwoHeaderNodeIndex); + PermanentGraphLayoutBuilder builder = new PermanentGraphLayoutBuilder(graph, heads, dfsUtil); + return builder.build(); + } + + private final PermanentGraph myGraph; + private final int[] myLayoutIndex; + + private final List myHeadNodeIndex; + private final int[] myStartLayoutIndexForHead; + + private final DfsUtil myDfsUtil; + + private int currentLayoutIndex = 1; + + private PermanentGraphLayoutBuilder(PermanentGraph graph, List headNodeIndex, DfsUtil dfsUtil) { + myGraph = graph; + myDfsUtil = dfsUtil; + myLayoutIndex = new int[graph.nodesCount()]; + + myHeadNodeIndex = headNodeIndex; + myStartLayoutIndexForHead = new int[headNodeIndex.size()]; + + } + + private void dfs(int nodeIndex) { + myDfsUtil.nodeDfsIterator(nodeIndex, new DfsUtil.NextNode() { + @Override + public int fun(int currentNode) { + boolean firstVisit = myLayoutIndex[currentNode] == 0; + if (firstVisit) + myLayoutIndex[currentNode] = currentLayoutIndex; + + int childWithoutLayoutIndex = -1; + for (int childNodeIndex : myGraph.getDownNodes(currentNode)) { + if (childNodeIndex != SomeGraph.NOT_LOAD_COMMIT && myLayoutIndex[childNodeIndex] == 0) { + childWithoutLayoutIndex = childNodeIndex; + break; + } + } + + if (childWithoutLayoutIndex == -1) { + if (firstVisit) + currentLayoutIndex++; + + return DfsUtil.NextNode.NODE_NOT_FOUND; + } else { + return childWithoutLayoutIndex; + } + } + }); + } + + @NotNull + private PermanentGraphLayout build() { + for(int i = 0; i < myHeadNodeIndex.size(); i++) { + int headNodeIndex = myHeadNodeIndex.get(i); + myStartLayoutIndexForHead[i] = currentLayoutIndex; + + dfs(headNodeIndex); + } + + return new PermanentGraphLayoutImpl(myLayoutIndex, myHeadNodeIndex, myStartLayoutIndexForHead); + } + + + + + private static class PermanentGraphLayoutImpl implements PermanentGraphLayout { + private final int[] myLayoutIndex; + + private final List myHeadNodeIndex; + private final int[] myStartLayoutIndexForHead; + + private PermanentGraphLayoutImpl(int[] layoutIndex, List headNodeIndex, int[] startLayoutIndexForHead) { + myLayoutIndex = layoutIndex; + myHeadNodeIndex = headNodeIndex; + myStartLayoutIndexForHead = startLayoutIndexForHead; + } + + @Override + public int getLayoutIndex(int nodeIndex) { + return myLayoutIndex[nodeIndex]; + } + + @Override + public int getOneOfHeadNodeIndex(int nodeIndex) { + return getHeadNodeIndex(getLayoutIndex(nodeIndex)); + } + + @Override + public int getHeadNodeIndex(int layoutIndex) { + return myHeadNodeIndex.get(getHeadOrder(layoutIndex)); + } + + private int getHeadOrder(int layoutIndex) { + int a = 0; + int b = myStartLayoutIndexForHead.length - 1; + while (b > a) { + int middle = (a + b + 1) / 2; + if (myStartLayoutIndexForHead[middle] <= layoutIndex) + a = middle; + else + b = middle - 1; + } + return a; + } + + @Override + public int getStartLayout(int layoutIndex) { + return myStartLayoutIndexForHead[getHeadOrder(layoutIndex)]; + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/ElementColorManager.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/ElementColorManager.java new file mode 100644 index 000000000000..d75826a14543 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/ElementColorManager.java @@ -0,0 +1,29 @@ +/* + * 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.vcs.log.newgraph.render; + +import com.intellij.ui.JBColor; +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import org.jetbrains.annotations.NotNull; + +/** + * @author erokhins + */ +public interface ElementColorManager { + + @NotNull + JBColor getColor(@NotNull GraphElement element); +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/GraphCellGenerator.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/GraphCellGenerator.java new file mode 100644 index 000000000000..f2c2d1a1e3e9 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/GraphCellGenerator.java @@ -0,0 +1,32 @@ +/* + * 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.vcs.log.newgraph.render; + +import com.intellij.vcs.log.newgraph.render.cell.GraphCell; + +/** + * @author erokhins + */ +public interface GraphCellGenerator { + int getCountVisibleRow(); + + GraphCell getGraphCell(int visibleRowIndex); + + boolean isShowLongEdges(); + void setShowLongEdges(boolean showLongEdges); + + void invalidate(); +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/GraphCellPainter.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/GraphCellPainter.java new file mode 100644 index 000000000000..d03ed0806dc6 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/GraphCellPainter.java @@ -0,0 +1,40 @@ +/* + * 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.vcs.log.newgraph.render; + +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import com.intellij.vcs.log.newgraph.render.cell.GraphCell; +import com.intellij.vcs.log.newgraph.render.cell.SpecialRowElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; + +/** + * @author erokhins + */ +public interface GraphCellPainter { + + void draw(@NotNull Graphics2D g2, @NotNull GraphCell graphCell); + + @Nullable + GraphElement mouseOver(@NotNull GraphCell graphCell, int x, int y); + + @Nullable + SpecialRowElement mouseOverArrow(@NotNull GraphCell graphCell, int x, int y); + +} + diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/GraphRender.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/GraphRender.java new file mode 100644 index 000000000000..6ff5fc8e1fad --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/GraphRender.java @@ -0,0 +1,125 @@ +/* + * 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.vcs.log.newgraph.render; + +import com.intellij.openapi.util.Pair; +import com.intellij.util.ui.UIUtil; +import com.intellij.vcs.log.graph.PaintInfo; +import com.intellij.vcs.log.newgraph.gpaph.MutableGraph; +import com.intellij.vcs.log.newgraph.render.cell.GraphCell; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; +import java.awt.image.BufferedImage; + +import static com.intellij.vcs.log.graph.render.PrintParameters.HEIGHT_CELL; +import static com.intellij.vcs.log.graph.render.PrintParameters.WIDTH_NODE; + +public class GraphRender { + + @NotNull + private final GraphCellGenerator myCellGenerator; + + @NotNull + private final GraphCellPainter myCellPainter; + + public GraphRender(@NotNull MutableGraph mutableGraph, @NotNull ElementColorManager colorManager, @NotNull GraphCellGenerator cellGenerator) { + myCellGenerator = cellGenerator; + myCellPainter = new SimpleGraphCellPainter(mutableGraph.getThickHoverController(), colorManager); + } + + @NotNull + public PaintInfo paint(int visibleRowIndex) { + Pair imageAndBufferImageWidth = getImageAndBufferImageWidth(visibleRowIndex); + BufferedImage image = UIUtil.createImage(imageAndBufferImageWidth.getSecond(), HEIGHT_CELL, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = image.createGraphics(); + + GraphCell graphCell = myCellGenerator.getGraphCell(visibleRowIndex); + myCellPainter.draw(g2, graphCell); + + return new PaintInfoImpl(image, imageAndBufferImageWidth.getFirst()); + } + + public boolean isShowLongEdges() { + return myCellGenerator.isShowLongEdges(); + } + + public void setShowLongEdges(boolean longEdgesHidden) { + myCellGenerator.setShowLongEdges(longEdgesHidden); + } + + public void invalidate() { + myCellGenerator.invalidate(); + } + + @NotNull + public GraphCellGenerator getCellGenerator() { + return myCellGenerator; + } + + @NotNull + public GraphCellPainter getCellPainter() { + return myCellPainter; + } + + @NotNull + private Pair getImageAndBufferImageWidth(int visibleRowIndex) { + int imageWidth = calcImageWidth(visibleRowIndex); + int bufferWidth = calcMaxImageWith(visibleRowIndex); + + if (bufferWidth > imageWidth + 30) { + imageWidth += (bufferWidth - imageWidth) / 4; + } + return new Pair(imageWidth, bufferWidth); + } + + private int calcImageWidth(int visibleRowIndex) { + return myCellGenerator.getGraphCell(visibleRowIndex).getCountElements() * WIDTH_NODE; + } + + private int calcMaxImageWith(int visibleRowIndex) { + int maxElementsCount = myCellGenerator.getGraphCell(visibleRowIndex).getCountElements(); + if (visibleRowIndex > 0) { + maxElementsCount = Math.max(maxElementsCount, myCellGenerator.getGraphCell(visibleRowIndex - 1).getCountElements()); + } + if (visibleRowIndex < myCellGenerator.getCountVisibleRow() - 1) { + maxElementsCount = Math.max(maxElementsCount, myCellGenerator.getGraphCell(visibleRowIndex + 1).getCountElements()); + } + return WIDTH_NODE * maxElementsCount; + } + + private static class PaintInfoImpl implements PaintInfo { + + private final Image myImage; + private final int myWidth; + + public PaintInfoImpl(Image image, int width) { + myImage = image; + myWidth = width; + } + + @NotNull + @Override + public Image getImage() { + return myImage; + } + + @Override + public int getWidth() { + return myWidth; + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/PositionUtil.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/PositionUtil.java new file mode 100644 index 000000000000..da4768837e7e --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/PositionUtil.java @@ -0,0 +1,82 @@ +/* + * 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.vcs.log.newgraph.render; + +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import com.intellij.vcs.log.newgraph.gpaph.Node; +import com.intellij.vcs.log.newgraph.render.cell.GraphCell; +import com.intellij.vcs.log.newgraph.render.cell.ShortEdge; +import com.intellij.vcs.log.newgraph.render.cell.SpecialRowElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; + +import static com.intellij.vcs.log.graph.render.PrintParameters.*; + +public class PositionUtil { + private static float distance(int x1, int y1, int x2, int y2) { + return (float)Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + } + + public static boolean overUpEdge(ShortEdge edge, int x, int y) { + float thick = THICK_LINE; + int x1 = WIDTH_NODE * edge.getDownPosition() + WIDTH_NODE / 2; + int y1 = HEIGHT_CELL / 2; + int x2 = WIDTH_NODE * edge.getUpPosition() + WIDTH_NODE / 2; + int y2 = -HEIGHT_CELL / 2; + //return true; + return (distance(x1, y1, x, y) + distance(x2, y2, x, y) < distance(x1, y1, x2, y2) + thick); + } + + public static boolean overDownEdge(ShortEdge edge, int x, int y) { + float thick = THICK_LINE; + int x1 = WIDTH_NODE * edge.getUpPosition() + WIDTH_NODE / 2; + int y1 = HEIGHT_CELL / 2; + int x2 = WIDTH_NODE * edge.getDownPosition() + WIDTH_NODE / 2; + int y2 = HEIGHT_CELL + HEIGHT_CELL / 2; + 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) { + int x0 = WIDTH_NODE * position + WIDTH_NODE / 2; + int y0 = HEIGHT_CELL / 2; + int r = CIRCLE_RADIUS; + return distance(x0, y0, x, y) <= r; + } + + public static int getYInsideRow(@NotNull Point point) { + return point.y - getRowIndex(point) * HEIGHT_CELL; + } + + public static int getRowIndex(@NotNull Point point) { + return point.y / HEIGHT_CELL; + } + + @Nullable + public static Node getNode(@Nullable GraphCell cell) { + if (cell == null) { + return null; + } + for (SpecialRowElement element : cell.getSpecialRowElements()) { + GraphElement graphElement = element.getElement(); + if (graphElement instanceof Node) { + return (Node) graphElement; + } + } + return null; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/SimpleGraphCellPainter.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/SimpleGraphCellPainter.java new file mode 100644 index 000000000000..e6fa1a00bab0 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/SimpleGraphCellPainter.java @@ -0,0 +1,298 @@ +/* + * 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.vcs.log.newgraph.render; + +import com.intellij.ui.JBColor; +import com.intellij.vcs.log.graph.render.PrintParameters; +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import com.intellij.vcs.log.newgraph.gpaph.Node; +import com.intellij.vcs.log.newgraph.gpaph.ThickHoverController; +import com.intellij.vcs.log.newgraph.render.cell.GraphCell; +import com.intellij.vcs.log.newgraph.render.cell.ShortEdge; +import com.intellij.vcs.log.newgraph.render.cell.SpecialRowElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.awt.geom.Ellipse2D; + +/** + * @author erokhins + */ +public class SimpleGraphCellPainter implements GraphCellPainter { + + private static final Color MARK_COLOR = JBColor.BLACK; + + private final Stroke usual = new BasicStroke(PrintParameters.THICK_LINE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL); + private final Stroke hide = new BasicStroke(PrintParameters.THICK_LINE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL, 0, new float[]{7}, 0); + private final Stroke selectUsual = new BasicStroke(PrintParameters.SELECT_THICK_LINE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL); + private final Stroke selectHide = new BasicStroke(PrintParameters.SELECT_THICK_LINE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL, 0, new float[]{7}, 0); + + private Graphics2D g2; + + @NotNull + private final ThickHoverController myThickHoverController; + + @NotNull + private final ElementColorManager myColorManager; + + public SimpleGraphCellPainter(@NotNull ThickHoverController thickHoverController, @NotNull ElementColorManager colorManager) { + myThickHoverController = thickHoverController; + myColorManager = colorManager; + } + + private void paintUpLine(int from, int to, Color color) { + int x1 = PrintParameters.WIDTH_NODE * from + PrintParameters.WIDTH_NODE / 2; + int y1 = PrintParameters.HEIGHT_CELL / 2; + int x2 = PrintParameters.WIDTH_NODE * to + PrintParameters.WIDTH_NODE / 2; + int y2 = -PrintParameters.HEIGHT_CELL / 2; + g2.setColor(color); + g2.drawLine(x2, y2, x1, y1); + } + + private void paintDownLine(int from, int to, Color color) { + int x1 = PrintParameters.WIDTH_NODE * from + PrintParameters.WIDTH_NODE / 2; + int y1 = PrintParameters.HEIGHT_CELL / 2; + int x2 = PrintParameters.WIDTH_NODE * to + PrintParameters.WIDTH_NODE / 2; + int y2 = PrintParameters.HEIGHT_CELL + PrintParameters.HEIGHT_CELL / 2; + g2.setColor(color); + g2.drawLine(x1, y1, x2, y2); + } + + private void paintAbove(int position, Color color) { + int x1 = PrintParameters.WIDTH_NODE * position + 3; + int y = 4; + int x2 = PrintParameters.WIDTH_NODE * position + PrintParameters.WIDTH_NODE - 4; + g2.setColor(color); + g2.drawLine(x1, y, x2, y); + } + + private void paintBelow(int position, Color color) { + int x1 = PrintParameters.WIDTH_NODE * position + 3; + int y = PrintParameters.HEIGHT_CELL - 4; + int x2 = PrintParameters.WIDTH_NODE * position + PrintParameters.WIDTH_NODE - 4; + g2.setColor(color); + g2.drawLine(x1, y, x2, y); + } + + + private void paintCircle(int position, Color color, boolean select) { + int x0 = PrintParameters.WIDTH_NODE * position + PrintParameters.WIDTH_NODE / 2; + int y0 = PrintParameters.HEIGHT_CELL / 2; + int r = PrintParameters.CIRCLE_RADIUS; + if (select) { + r = PrintParameters.SELECT_CIRCLE_RADIUS; + } + Ellipse2D.Double circle = new Ellipse2D.Double(x0 - r + 0.5, y0 - r + 0.5, 2 * r, 2 * r); + g2.setColor(color); + g2.fill(circle); + } + + private void paintHide(int position, Color color) { + int x0 = PrintParameters.WIDTH_NODE * position + PrintParameters.WIDTH_NODE / 2; + int y0 = PrintParameters.HEIGHT_CELL / 2; + int r = PrintParameters.CIRCLE_RADIUS; + g2.setColor(color); + g2.drawLine(x0, y0, x0, y0 + r); + g2.drawLine(x0, y0 + r, x0 + r, y0); + g2.drawLine(x0, y0 + r, x0 - r, y0); + } + + private void paintShow(int position, Color color) { + int x0 = PrintParameters.WIDTH_NODE * position + PrintParameters.WIDTH_NODE / 2; + int y0 = PrintParameters.HEIGHT_CELL / 2; + int r = PrintParameters.CIRCLE_RADIUS; + g2.setColor(color); + g2.drawLine(x0, y0, x0, y0 - r); + g2.drawLine(x0, y0 - r, x0 + r, y0); + g2.drawLine(x0, y0 - r, x0 - r, y0); + } + + private void paintDownHarmonica(Color color) { + int x0 = 0; + int y0 = PrintParameters.HEIGHT_CELL - 1; + g2.setColor(color); + g2.drawLine(x0, y0, x0 + PrintParameters.WIDTH_NODE / 2 , y0); + } + + private void setStroke(boolean usual, boolean select) { + if (usual) { + if (select) { + g2.setStroke(selectUsual); + } + else { + g2.setStroke(this.usual); + } + } + else { + if (select) { + g2.setStroke(selectHide); + } + else { + g2.setStroke(hide); + } + } + } + + private interface LitePrinter { + void print(Color color); + } + + private void drawLogic(boolean selected, boolean marked, boolean isUsual, Color usualColor, LitePrinter printer) { + if (marked) { + setStroke(isUsual, true); + printer.print(MARK_COLOR); + setStroke(isUsual, false); + printer.print(usualColor); + } else { + setStroke(isUsual, selected); + printer.print(usualColor); + } + } + + + private boolean isSelected(@NotNull GraphElement graphElement) { + return myThickHoverController.isThick(graphElement); + } + + private boolean isMarked(@NotNull GraphElement graphElement) { + return myThickHoverController.isHover(graphElement); + } + + private static boolean isUsual(@NotNull Edge edge) { + return edge.getType() == Edge.Type.USUAL; + } + + @NotNull + private JBColor getColor(@NotNull GraphElement element) { + return myColorManager.getColor(element); + } + + private boolean isMarked(@NotNull SpecialRowElement specialRowElement) { + return myThickHoverController.isHover(specialRowElement) || myThickHoverController.isHover(specialRowElement.getElement()); + } + + @Override + public void draw(@NotNull Graphics2D g2, @NotNull GraphCell row) { + this.g2 = g2; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + for (final ShortEdge shortEdge : row.getUpEdges()) { + Edge edge = shortEdge.getEdge(); + drawLogic(isSelected(edge), isMarked(edge), isUsual(edge), getColor(edge), new LitePrinter() { + @Override + public void print(Color color) { + paintUpLine(shortEdge.getDownPosition(), shortEdge.getUpPosition(), color); + } + }); + } + for (final ShortEdge shortEdge : row.getDownEdges()) { + Edge edge = shortEdge.getEdge(); + drawLogic(isSelected(edge), isMarked(edge), isUsual(edge), getColor(edge), new LitePrinter() { + @Override + public void print(Color color) { + paintDownLine(shortEdge.getUpPosition(), shortEdge.getDownPosition(), color); + } + }); + + } + for (final SpecialRowElement rowElement : row.getSpecialRowElements()) { + GraphElement element = rowElement.getElement(); + final Edge edge; + switch (rowElement.getType()) { + case NODE: + assert element instanceof Node; + Node node = (Node) element; + if (isMarked(node)) { + paintCircle(rowElement.getPosition(), MARK_COLOR, true); + paintCircle(rowElement.getPosition(), getColor(node), false); + } else { + paintCircle(rowElement.getPosition(), getColor(node), isSelected(node)); + } + break; + case UP_ARROW: + assert element instanceof Edge; + edge = (Edge) element; + drawLogic(isSelected(edge), isMarked(rowElement), isUsual(edge), getColor(edge), new LitePrinter() { + @Override + public void print(Color color) { + paintShow(rowElement.getPosition(), color); + } + }); + break; + case DOWN_ARROW: + assert element instanceof Edge; + edge = (Edge) element; + drawLogic(isSelected(edge), isMarked(rowElement), isUsual(edge), getColor(edge), new LitePrinter() { + @Override + public void print(Color color) { + paintHide(rowElement.getPosition(), color); + } + }); + break; + case DOWN_HARMONICA: + drawLogic(true, false, true, JBColor.BLACK, new LitePrinter() { + @Override + public void print(Color color) { + //paintDownHarmonica(color); + } + }); + break; + default: + throw new IllegalStateException(); + } + } + + } + + @Nullable + @Override + public GraphElement mouseOver(@NotNull GraphCell graphCell, int x, int y) { + for (SpecialRowElement printElement : graphCell.getSpecialRowElements()) { + if (printElement.getType() == SpecialRowElement.Type.NODE) { + if (PositionUtil.overNode(printElement.getPosition(), x, y)) { + return printElement.getElement(); + } + } + } + for (ShortEdge edge : graphCell.getUpEdges()) { + if (PositionUtil.overUpEdge(edge, x, y)) { + return edge.getEdge(); + } + } + for (ShortEdge edge : graphCell.getDownEdges()) { + if (PositionUtil.overDownEdge(edge, x, y)) { + return edge.getEdge(); + } + } + + return null; + } + + @Nullable + @Override + public SpecialRowElement mouseOverArrow(@NotNull GraphCell graphCell, int x, int y) { + for (SpecialRowElement rowElement : graphCell.getSpecialRowElements()) { + if (rowElement.getType() == SpecialRowElement.Type.UP_ARROW || rowElement.getType() == SpecialRowElement.Type.DOWN_ARROW) { + if (PositionUtil.overNode(rowElement.getPosition(), x, y)) { + return rowElement; + } + } + } + return null; + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/AbstractGraphCellGenerator.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/AbstractGraphCellGenerator.java new file mode 100644 index 000000000000..7035c1c245d2 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/AbstractGraphCellGenerator.java @@ -0,0 +1,64 @@ +/* + * 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.vcs.log.newgraph.render.cell; + +import com.intellij.vcs.log.newgraph.gpaph.MutableGraph; +import com.intellij.vcs.log.newgraph.render.GraphCellGenerator; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public abstract class AbstractGraphCellGenerator implements GraphCellGenerator { + @NotNull + protected final MutableGraph myGraph; + + protected AbstractGraphCellGenerator(@NotNull MutableGraph graph) { + myGraph = graph; + } + + public int getCountVisibleRow() { + return myGraph.getCountVisibleNodes(); + } + + public GraphCell getGraphCell(int visibleRowIndex) { + List downShortEdges; + List upShortEdges; + if (visibleRowIndex == getCountVisibleRow() - 1) + downShortEdges = Collections.emptyList(); + else + downShortEdges = getDownShortEdges(visibleRowIndex); + + if (visibleRowIndex == 0) + upShortEdges = Collections.emptyList(); + else + upShortEdges = getDownShortEdges(visibleRowIndex - 1); + + return new GraphCell(getCountVisibleElements(visibleRowIndex), + getSpecialElements(visibleRowIndex), + upShortEdges, + downShortEdges); + } + + protected abstract int getCountVisibleElements(int visibleRowIndex); + + // visibleRowIndex in [0, getCountVisibleRow() - 2] + @NotNull + protected abstract List getDownShortEdges(int visibleRowIndex); + + @NotNull + protected abstract List getSpecialElements(int visibleRowIndex); +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/EdgesInRow.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/EdgesInRow.java new file mode 100644 index 000000000000..2e286d399e20 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/EdgesInRow.java @@ -0,0 +1,129 @@ +/* + * 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.vcs.log.newgraph.render.cell; + +import com.intellij.util.containers.SLRUMap; +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import com.intellij.vcs.log.newgraph.gpaph.MutableGraph; +import com.intellij.vcs.log.newgraph.gpaph.Node; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +public class EdgesInRow { + private final static int CACHE_SIZE = 100; + private final static int WALK_SIZE = 1000; + + @NotNull + private final MutableGraph myGraph; + + @NotNull + private final SLRUMap> cache = new SLRUMap>(CACHE_SIZE, CACHE_SIZE * 2); + + public EdgesInRow(@NotNull MutableGraph graph) { + myGraph = graph; + } + + @NotNull + public Set getEdgesInRow(int visibleRowIndex) { + Set edges = cache.get(visibleRowIndex); + if (edges != null) { + return edges; + } + + return calculateEdgesAround(visibleRowIndex); + } + + public void invalidate() { + cache.clear(); + } + + @NotNull + private Set calculateEdgesAround(int visibleRowIndex) { + int startIndex = Math.max(visibleRowIndex - CACHE_SIZE / 2, 0); + int endIndex = Math.min(visibleRowIndex + CACHE_SIZE / 2, myGraph.getCountVisibleNodes() - 1); + List> uCorrectEdges = getUCorrectEdges(startIndex, endIndex); + List> dCorrectEdges = getDCorrectEdges(startIndex, endIndex); + + assert uCorrectEdges.size() == dCorrectEdges.size(); + + for (int i = 0; i < uCorrectEdges.size(); i++) + uCorrectEdges.get(i).addAll(dCorrectEdges.get(i)); + + for (int i = 0; i < uCorrectEdges.size(); i++) + cache.put(startIndex + i, uCorrectEdges.get(i)); + + return uCorrectEdges.get(visibleRowIndex - startIndex); + } + + // [start, end] + @NotNull + private List> getUCorrectEdges(int startIndex, int endIndex) { + int startCalculateIndex = Math.max(startIndex - WALK_SIZE, 0); + List> result = new ArrayList>(endIndex - startIndex + 1); + Node currentNode = myGraph.getNode(startCalculateIndex); + Set edgesInCurrentRow = new HashSet(); + + if (startCalculateIndex >= startIndex) + result.add(new HashSet(edgesInCurrentRow)); + + for (int i = startCalculateIndex + 1; i <= endIndex; i++) { + Node nextNode = myGraph.getNode(i); + oneDownStep(edgesInCurrentRow, currentNode, nextNode); + + if (i >= startIndex) + result.add(new HashSet(edgesInCurrentRow)); + + currentNode = nextNode; + } + return result; + } + + private static void oneDownStep(Set edgesInCurrentRow, Node currentNode, Node nextNode) { + edgesInCurrentRow.addAll(currentNode.getDownEdges()); + edgesInCurrentRow.removeAll(nextNode.getUpEdges()); + } + + // [start, end] + @NotNull + private List> getDCorrectEdges(int startIndex, int endIndex) { + int endCalculateIndex = Math.min(endIndex + WALK_SIZE, myGraph.getCountVisibleNodes() - 1); + List> result = new ArrayList>(endIndex - startIndex + 1); + Node currentNode = myGraph.getNode(endCalculateIndex); + Set edgesInCurrentRow = new HashSet(); + + if (endCalculateIndex <= endIndex) + result.add(new HashSet(edgesInCurrentRow)); + + for (int i = endCalculateIndex - 1; i >= startIndex; i--) { + Node prevNode = myGraph.getNode(i); + oneUpStep(edgesInCurrentRow, currentNode, prevNode); + + if (i <= endIndex) + result.add(new HashSet(edgesInCurrentRow)); + + currentNode = prevNode; + } + + Collections.reverse(result); + return result; + } + + private static void oneUpStep(Set edgesInCurrentRow, Node currentNode, Node prevNode) { + edgesInCurrentRow.addAll(currentNode.getUpEdges()); + edgesInCurrentRow.removeAll(prevNode.getDownEdges()); + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/FilterGraphCellGenerator.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/FilterGraphCellGenerator.java new file mode 100644 index 000000000000..fc698ef24dd6 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/FilterGraphCellGenerator.java @@ -0,0 +1,80 @@ +/* + * 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.vcs.log.newgraph.render.cell; + +import com.intellij.vcs.log.newgraph.gpaph.Node; +import com.intellij.vcs.log.newgraph.gpaph.impl.FilterMutableGraph; +import com.intellij.vcs.log.newgraph.render.GraphCellGenerator; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class FilterGraphCellGenerator implements GraphCellGenerator { + + @NotNull + private final GraphCellGenerator myDelegateCellGenerator; + + @NotNull + private final FilterMutableGraph myFilterMutableGraph; + + + public FilterGraphCellGenerator(@NotNull FilterMutableGraph filterMutableGraph) { + myFilterMutableGraph = filterMutableGraph; + myDelegateCellGenerator = new GraphCellGeneratorImpl(filterMutableGraph); + } + + @Override + public int getCountVisibleRow() { + return myDelegateCellGenerator.getCountVisibleRow(); + } + + @Override + public GraphCell getGraphCell(int visibleRowIndex) { + GraphCell delegateCell = myDelegateCellGenerator.getGraphCell(visibleRowIndex); + List newSpecialElements = new ArrayList(); + newSpecialElements.addAll(delegateCell.getSpecialRowElements()); + + Node node = null; + for (SpecialRowElement element : newSpecialElements) { + if (element.getType() == SpecialRowElement.Type.NODE) + node = (Node)element.getElement(); + } + assert node != null; + + if (myFilterMutableGraph.nextRowIsHide(visibleRowIndex)) + newSpecialElements.add(new SpecialRowElement(node, 0, SpecialRowElement.Type.DOWN_HARMONICA)); + + return new GraphCell(delegateCell.getCountElements(), newSpecialElements, delegateCell.getUpEdges(), delegateCell.getDownEdges()); + } + + @Override + public boolean isShowLongEdges() { + return myDelegateCellGenerator.isShowLongEdges(); + } + + @Override + public void setShowLongEdges(boolean showLongEdges) { + myDelegateCellGenerator.setShowLongEdges(showLongEdges); + } + + @Override + public void invalidate() { + myDelegateCellGenerator.invalidate(); + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/GraphCell.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/GraphCell.java new file mode 100644 index 000000000000..e8a34fb18d27 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/GraphCell.java @@ -0,0 +1,62 @@ +/* + * 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.vcs.log.newgraph.render.cell; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class GraphCell { + private final int myCountElements; + + @NotNull + private final List mySpecialRowElements; + + @NotNull + private final List myUpEdges; + + @NotNull + private final List myDownEdges; + + public GraphCell(int countElements, + @NotNull List specialRowElements, + @NotNull List upEdges, + @NotNull List downEdges) { + myCountElements = countElements; + mySpecialRowElements = specialRowElements; + myUpEdges = upEdges; + myDownEdges = downEdges; + } + + public int getCountElements() { + return myCountElements; + } + + @NotNull + public List getSpecialRowElements() { + return mySpecialRowElements; + } + + @NotNull + public List getUpEdges() { + return myUpEdges; + } + + @NotNull + public List getDownEdges() { + return myDownEdges; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/GraphCellGeneratorImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/GraphCellGeneratorImpl.java new file mode 100644 index 000000000000..57a608c16ff5 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/GraphCellGeneratorImpl.java @@ -0,0 +1,245 @@ +/* + * 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.vcs.log.newgraph.render.cell; + +import com.intellij.util.Function; +import com.intellij.util.SmartList; +import com.intellij.util.containers.SLRUMap; +import com.intellij.vcs.log.newgraph.SomeGraph; +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import com.intellij.vcs.log.newgraph.gpaph.MutableGraph; +import com.intellij.vcs.log.newgraph.gpaph.Node; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +public class GraphCellGeneratorImpl extends AbstractGraphCellGenerator { + private static final int LONG_EDGE_SIZE = 30; + private static final int LONG_EDGE_PART_SIZE = 1; + + private static final int VERY_LONG_EDGE_SIZE = 1000; + private static final int VERY_LONG_EDGE_PART_SIZE = 250; + private static final int CACHE_SIZE = 100; + private static final boolean SHOW_ARROW_WHEN_SHOW_LONG_EDGES = true; + + + protected final EdgesInRow myEdgesInRow; + private final SLRUMap> cache = new SLRUMap>(CACHE_SIZE, CACHE_SIZE * 2); + + private boolean showLongEdges = false; + + public GraphCellGeneratorImpl(@NotNull MutableGraph graph) { + super(graph); + myEdgesInRow = new EdgesInRow(graph); + } + + @Override + protected int getCountVisibleElements(int visibleRowIndex) { + return getSortedVisibleElementsInRow(visibleRowIndex).size(); + } + + @NotNull + @Override + protected List getDownShortEdges(int visibleRowIndex) { + Function endPosition = createEndPositionFunction(visibleRowIndex); + + List result = new ArrayList(); + List visibleElements = getSortedVisibleElementsInRow(visibleRowIndex); + + for (int startPosition = 0; startPosition < visibleElements.size(); startPosition++) { + GraphElement element = visibleElements.get(startPosition); + if (element instanceof Node) { + Node node = (Node) element; + for (Edge edge : node.getDownEdges()) { + int endPos = endPosition.fun(edge); + if (endPos != -1) + result.add(new ShortEdge(edge, startPosition, endPos)); + } + } + + if (element instanceof Edge) { + Edge edge = (Edge) element; + int endPos = endPosition.fun(edge); + if (endPos != -1) + result.add(new ShortEdge(edge, startPosition, endPos)); + } + } + + return result; + } + + @NotNull + private Function createEndPositionFunction(int visibleRowIndex) { + List visibleElementsInNextRow = getSortedVisibleElementsInRow(visibleRowIndex + 1); + + final Map toPosition = new HashMap(); + for (int position = 0; position < visibleElementsInNextRow.size(); position++) + toPosition.put(visibleElementsInNextRow.get(position), position); + + return new Function() { + @Override + public Integer fun(Edge edge) { + Integer position = toPosition.get(edge); + if (position == null) { + int downNodeVisibleIndex = edge.getDownNodeVisibleIndex(); + if (downNodeVisibleIndex != SomeGraph.NOT_LOAD_COMMIT) + position = toPosition.get(myGraph.getNode(downNodeVisibleIndex)); + } + + if (position == null) { + // i.e. is long edge with arrow + return -1; + } else { + return position; + } + } + }; + } + + @NotNull + @Override + protected List getSpecialElements(int visibleRowIndex) { + List result = new SmartList(); + int position = 0; + for (GraphElement element : getSortedVisibleElementsInRow(visibleRowIndex)) { + if (element instanceof Node) { + result.add(new SpecialRowElement(element, position, SpecialRowElement.Type.NODE)); + } + if (element instanceof Edge) { + Edge edge = (Edge) element; + int edgeSize = edge.getDownNodeVisibleIndex() - edge.getUpNodeVisibleIndex(); + int upOffset = visibleRowIndex - edge.getUpNodeVisibleIndex(); + int downOffset = edge.getDownNodeVisibleIndex() - visibleRowIndex; + + if (edgeSize >= LONG_EDGE_SIZE) { + if (!showLongEdges) { + addArrowIfNeeded(result, position, edge, upOffset, downOffset, LONG_EDGE_PART_SIZE); + } else { + if (SHOW_ARROW_WHEN_SHOW_LONG_EDGES) + addArrowIfNeeded(result, position, edge, upOffset, downOffset, LONG_EDGE_PART_SIZE); + + if (edgeSize >= VERY_LONG_EDGE_SIZE) + addArrowIfNeeded(result, position, edge, upOffset, downOffset, VERY_LONG_EDGE_PART_SIZE); + } + } + + } + position++; + } + return result; + } + + private static void addArrowIfNeeded(List result, + int position, + Edge edge, + int upOffset, + int downOffset, + int edgePartSize) { + if (upOffset == edgePartSize) + result.add(new SpecialRowElement(edge, position, SpecialRowElement.Type.DOWN_ARROW)); + + if (downOffset == edgePartSize) + result.add(new SpecialRowElement(edge, position, SpecialRowElement.Type.UP_ARROW)); + } + + @Override + public boolean isShowLongEdges() { + return showLongEdges; + } + + @Override + public void setShowLongEdges(boolean showLongEdges) { + this.showLongEdges = showLongEdges; + invalidate(); + } + + @Override + public void invalidate() { + myEdgesInRow.invalidate(); + cache.clear(); + } + + private int getLongEdgeSize() { + if (showLongEdges) + return VERY_LONG_EDGE_SIZE; + else + return LONG_EDGE_SIZE; + } + + private int getEdgeShowPartSize() { + if (showLongEdges) + return VERY_LONG_EDGE_PART_SIZE; + else + return LONG_EDGE_PART_SIZE; + } + + private boolean edgeIsVisibleInRow(@NotNull Edge edge, int visibleRowIndex) { + int edgeSize = edge.getDownNodeVisibleIndex() - edge.getUpNodeVisibleIndex(); + if (edgeSize < getLongEdgeSize()) { + return true; + } else { + return visibleRowIndex - edge.getUpNodeVisibleIndex() <= getEdgeShowPartSize() + || edge.getDownNodeVisibleIndex() - visibleRowIndex <= getEdgeShowPartSize(); + } + } + + @NotNull + private List getSortedVisibleElementsInRow(int visibleRowIndex) { + List graphElements = cache.get(visibleRowIndex); + if (graphElements != null) { + return graphElements; + } + + List result = new ArrayList(); + result.add(myGraph.getNode(visibleRowIndex)); + + for (Edge edge : myEdgesInRow.getEdgesInRow(visibleRowIndex)) { + if (edgeIsVisibleInRow(edge, visibleRowIndex)) + result.add(edge); + } + + Collections.sort(result, new Comparator() { + @Override + public int compare(@NotNull GraphElement o1, @NotNull GraphElement o2) { + int layoutIndex1 = o1.getLayoutIndex(); + int layoutIndex2 = o2.getLayoutIndex(); + if (layoutIndex1 != layoutIndex2) + return layoutIndex1 - layoutIndex2; + + if (o1 instanceof Node) + return 1; + + if (o2 instanceof Node) + return -1; + + if (o1 instanceof Edge && o2 instanceof Edge) { + Edge edge1 = (Edge)o1; + Edge edge2 = (Edge)o2; + if (edge1.getUpNodeVisibleIndex() != edge2.getUpNodeVisibleIndex()) + return edge1.getUpNodeVisibleIndex() - edge2.getUpNodeVisibleIndex(); + else + return edge2.getDownNodeVisibleIndex() - edge1.getDownNodeVisibleIndex(); + } + return 0; + } + }); + + cache.put(visibleRowIndex, result); + return result; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/ShortEdge.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/ShortEdge.java new file mode 100644 index 000000000000..7f564f90b6ac --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/ShortEdge.java @@ -0,0 +1,46 @@ +/* + * 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.vcs.log.newgraph.render.cell; + + +import com.intellij.vcs.log.newgraph.gpaph.Edge; +import org.jetbrains.annotations.NotNull; + +public class ShortEdge { + @NotNull + private final Edge myEdge; + private final int myUpPosition; + private final int myDownPosition; + + public ShortEdge(@NotNull Edge edge, int upPosition, int downPosition) { + myEdge = edge; + myUpPosition = upPosition; + myDownPosition = downPosition; + } + + public int getUpPosition() { + return myUpPosition; + } + + public int getDownPosition() { + return myDownPosition; + } + + @NotNull + public Edge getEdge() { + return myEdge; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/SpecialRowElement.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/SpecialRowElement.java new file mode 100644 index 000000000000..ed65fc024ff5 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/render/cell/SpecialRowElement.java @@ -0,0 +1,81 @@ +/* + * 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.vcs.log.newgraph.render.cell; + +import com.intellij.vcs.log.newgraph.gpaph.GraphElement; +import org.jetbrains.annotations.NotNull; + +public class SpecialRowElement { + @NotNull + private final GraphElement myElement; + + private final int myPosition; + + @NotNull + private final Type myType; + + public SpecialRowElement(@NotNull GraphElement element, int position, @NotNull Type type) { + myElement = element; + myPosition = position; + myType = type; + } + + @NotNull + public GraphElement getElement() { + return myElement; + } + + public int getPosition() { + return myPosition; + } + + @NotNull + public Type getType() { + return myType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SpecialRowElement)) return false; + + SpecialRowElement element = (SpecialRowElement)o; + + if (myPosition != element.myPosition) return false; + if (!myElement.equals(element.myElement)) return false; + if (myType != element.myType) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myElement.hashCode(); + result = 31 * result + myPosition; + result = 31 * result + myType.hashCode(); + return result; + } + + public enum Type { + NODE, + UP_ARROW, + DOWN_ARROW, + + DOWN_HARMONICA, + UP_HARMONICA + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/DfsUtil.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/DfsUtil.java new file mode 100644 index 000000000000..c4ee28e041e9 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/DfsUtil.java @@ -0,0 +1,48 @@ +/* + * 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.vcs.log.newgraph.utils; + +import org.jetbrains.annotations.NotNull; + +public class DfsUtil { + private final int[] myStack; + + public DfsUtil(int maxStackSize) { + myStack = new int[maxStackSize]; + } + + public interface NextNode { + int NODE_NOT_FOUND = -1; + + int fun(int currentNode); + } + + public void nodeDfsIterator(int startRowIndex, @NotNull NextNode nextNodeFun) { + myStack[0] = startRowIndex; + int stackIndex = 0; + + while (stackIndex >= 0) { + int nextNode = nextNodeFun.fun(myStack[stackIndex]); + if (nextNode != NextNode.NODE_NOT_FOUND) { + stackIndex++; + myStack[stackIndex] = nextNode; + } else { + stackIndex--; + } + } + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/Flags.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/Flags.java new file mode 100644 index 000000000000..8f7de2dcb3c3 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/Flags.java @@ -0,0 +1,25 @@ +/* + * 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.vcs.log.newgraph.utils; + +/** + * @author erokhins + */ +public interface Flags { + int size(); + boolean get(int index); + void set(int index, boolean value); +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/IntToIntMap.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/IntToIntMap.java new file mode 100644 index 000000000000..09f0d0c1e0be --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/IntToIntMap.java @@ -0,0 +1,31 @@ +/* + * 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.vcs.log.newgraph.utils; + +/** + * @author erokhins + */ +public interface IntToIntMap { + int shortSize(); + int longSize(); + int getLongIndex(int shortIndex); // must be very fast + + /** + * @param longIndex + * @return max shortIndex, witch getLongIndex(shortIndex) <= longIndex or 0 + */ + int getShortIndex(int longIndex); +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/MyUtils.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/MyUtils.java new file mode 100644 index 000000000000..2fbd5a09e54f --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/MyUtils.java @@ -0,0 +1,28 @@ +/* + * 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.vcs.log.newgraph.utils; + +import org.jetbrains.annotations.NotNull; + +public class MyUtils { + + public static void setAllValues(@NotNull Flags flags, boolean value) { + for (int i = 0; i < flags.size(); i++) + flags.set(i, value); + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/impl/AbstractIntToIntMap.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/impl/AbstractIntToIntMap.java new file mode 100644 index 000000000000..795f4c8e2ae7 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/impl/AbstractIntToIntMap.java @@ -0,0 +1,42 @@ +/* + * 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.vcs.log.newgraph.utils.impl; + +import com.intellij.vcs.log.newgraph.utils.IntToIntMap; + +public abstract class AbstractIntToIntMap implements IntToIntMap { + + @Override + public int getShortIndex(int longIndex) { + if (longIndex < 0 || longIndex >= longSize()) + throw new IndexOutOfBoundsException("LongSize is: " + longSize() + ", but longIndex: " + longIndex); + + if (shortSize() == 0 || getLongIndex(0) > longIndex) + return 0; + int a = 0; + int b = shortSize() - 1; + while (b > a + 1) { + int middle = (a + b) / 2; + if (getLongIndex(middle) <= longIndex) { + a = middle; + } else { + b = middle; + } + } + return getLongIndex(b) <= longIndex ? b : a; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/impl/IDIntToInt.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/impl/IDIntToInt.java new file mode 100644 index 000000000000..8a27c40937a5 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/impl/IDIntToInt.java @@ -0,0 +1,46 @@ +/* + * 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.vcs.log.newgraph.utils.impl; + +import com.intellij.vcs.log.newgraph.utils.IntToIntMap; + +public class IDIntToInt implements IntToIntMap { + private final int size; + + public IDIntToInt(int size) { + this.size = size; + } + + @Override + public int shortSize() { + return size; + } + + @Override + public int longSize() { + return size; + } + + @Override + public int getLongIndex(int shortIndex) { + return shortIndex; + } + + @Override + public int getShortIndex(int longIndex) { + return longIndex; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/impl/TreeIntToIntMap.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/impl/TreeIntToIntMap.java new file mode 100644 index 000000000000..b37f3ad7336d --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/newgraph/utils/impl/TreeIntToIntMap.java @@ -0,0 +1,138 @@ +/* + * 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.vcs.log.newgraph.utils.impl; + +import com.intellij.util.BooleanFunction; +import org.jetbrains.annotations.NotNull; + +public class TreeIntToIntMap extends AbstractIntToIntMap { + + public static TreeIntToIntMap newInstance(@NotNull final BooleanFunction thisIsVisible, final int longSize) { + if (longSize < 1) + throw new IllegalArgumentException("Unsupported size: " + longSize); + + int countLevels; // longSize -> countLevels: 1..2 -> 2; 3..4 -> 3; 5..8 -> 4 + if (longSize == 1) + countLevels = 2; + else + countLevels = countDigits(longSize - 1) + 1; + + int[] emptyTree = new int[(1<<(countLevels - 1))]; + TreeIntToIntMap intToIntMap = new TreeIntToIntMap(thisIsVisible, longSize, countLevels, emptyTree); + intToIntMap.update(0, longSize - 1); + return intToIntMap; + } + + private static int countDigits(int longSize) { + int count = 0; + while (longSize != 0) { + count++; + longSize >>= 1; + } + return count; + } + + @NotNull + private final BooleanFunction myThisIsVisible; + + private final int myLongSize; + private final int myCountLevels; + private final int[] myTree; + + private TreeIntToIntMap(@NotNull BooleanFunction thisIsVisible, int longSize, int countLevels, int[] tree) { + myThisIsVisible = thisIsVisible; + myLongSize = longSize; + myCountLevels = countLevels; + myTree = tree; + } + + + @Override + public int shortSize() { + return myTree[1]; + } + + @Override + public int longSize() { + return myLongSize; + } + + @Override + public int getLongIndex(int shortIndex) { + if (shortIndex < 0 || shortIndex >= shortSize()) + throw new IndexOutOfBoundsException("ShortSize is: " + shortSize() + ", but shortIndex: " + shortIndex); + + int node = 1; + for (int level = 0; level < myCountLevels - 1; level++) { + int child = node << 1; + int countInChildNode = getCountInNode(child); + if (countInChildNode > shortIndex) + node = child; + else { + node = child + 1; + shortIndex -= countInChildNode; + } + } + return node - myTree.length; + } + + public void update(int startLongIndex, int endLongIndex) { + if (startLongIndex < 0 || endLongIndex < startLongIndex || endLongIndex >= longSize()) + throw new IllegalArgumentException( + "ShortSize is: " + shortSize() + ", but updateRequest is: (" + startLongIndex +", " + endLongIndex + ")"); + + int startNode = startLongIndex + myTree.length; + int endNode = endLongIndex + myTree.length; + int commonNode = startNode >> countDigits(startNode ^ endNode); + updateNodeCount(commonNode); + + int parent = commonNode >> 1; + while (parent != 0) { + myTree[parent] = getCountInNode(parent << 1) + getCountInNode((parent << 1) + 1); + parent >>= 1; + } + } + + private boolean isLastLevel(int node) { + return node >= myTree.length; + } + + private int updateNodeCount(int node) { + if (isLastLevel(node)) + return getCountInLastLevel(node); + + int child = node << 1; + myTree[node] = updateNodeCount(child) + updateNodeCount(child + 1); + return myTree[node]; + } + + private int getCountInLastLevel(int node) { + node -= myTree.length; + if (node < myLongSize && myThisIsVisible.fun(node)) + return 1; + else + return 0; + } + + private int getCountInNode(int node) { + if (isLastLevel(node)) + return getCountInLastLevel(node); + else + return myTree[node]; + } + +} diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/GraphTestUtils.java b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/GraphTestUtils.java index a8c716628125..1f0922df699f 100644 --- a/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/GraphTestUtils.java +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/GraphTestUtils.java @@ -1,6 +1,5 @@ package com.intellij.vcs.log.graph; -import com.intellij.openapi.vfs.newvfs.impl.StubVirtualFile; import com.intellij.vcs.log.GraphCommit; import com.intellij.vcs.log.VcsRef; import com.intellij.vcs.log.graph.elements.Branch; @@ -60,7 +59,7 @@ public class GraphTestUtils { @NotNull public static Branch createBranchWithFakeRoot(int commitHash, @NotNull Collection refs) { - return new Branch(commitHash, refs, new StubVirtualFile()); + return new Branch(commitHash, refs, -1); } // "1 20 3" -> {1,20,3} diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/AbstractTestWithTextFile.java b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/AbstractTestWithTextFile.java new file mode 100644 index 000000000000..49382c22caed --- /dev/null +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/AbstractTestWithTextFile.java @@ -0,0 +1,42 @@ +/* + * 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.vcs.log.newgraph; + +import com.intellij.openapi.util.io.FileUtil; + +import java.io.File; +import java.io.IOException; + +public abstract class AbstractTestWithTextFile { + public static final String BASE_DIRECTORY = "platform/vcs-log/graph/testData/"; + public static final String IN_POSTFIX = "_in.txt"; + public static final String OUT_POSTFIX = "_out.txt"; + + protected final String myDirectory; + + protected AbstractTestWithTextFile(String directory) { + this.myDirectory = BASE_DIRECTORY + directory; + } + + protected void doTest(String testName) throws IOException { + String in = FileUtil.loadFile(new File(myDirectory + testName + IN_POSTFIX)); + String out = FileUtil.loadFile(new File(myDirectory + testName + OUT_POSTFIX)); + runTest(in, out); + } + + protected abstract void runTest(String in, String out); +} diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/GraphStrUtils.java b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/GraphStrUtils.java new file mode 100644 index 000000000000..3feac3328888 --- /dev/null +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/GraphStrUtils.java @@ -0,0 +1,137 @@ +/* + * 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.vcs.log.newgraph; + +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.newgraph.facade.ContainingBranchesGetter; + +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class GraphStrUtils { + + + public static > void appendSortList(List list, StringBuilder s) { + ArrayList sorted = new ArrayList(list); + Collections.sort(sorted); + appendList(sorted, s); + } + + public static void appendList(List list, StringBuilder s) { + + boolean first = true; + for (T element : list) { + if (first) { + first = false; + } else { + s.append(" "); + } + + s.append(element); + } + } + + public static String permanentGraphTorStr(PermanentGraph graph) { + StringBuilder s = new StringBuilder(); + for (int nodeIndex = 0; nodeIndex < graph.nodesCount(); nodeIndex++) { + if (nodeIndex != 0) + s.append("\n"); + + s.append(nodeIndex); + + s.append("|-"); + appendSortList(graph.getUpNodes(nodeIndex), s); + + s.append("|-"); + appendList(graph.getDownNodes(nodeIndex), s); + } + return s.toString(); + } + + public static String commitsWithNotLoadParentMapToStr(Map commitMap) { + List hashes = new ArrayList(commitMap.keySet()); + Collections.sort(hashes); + + StringBuilder s = new StringBuilder(); + for (int i = 0; i < hashes.size(); i++) { + if (i != 0) + s.append("\n"); + + Integer hash = hashes.get(i); + GraphCommit commit = commitMap.get(hash); + assertEquals(Integer.toHexString(hash), Integer.toHexString(commit.getIndex())); + + s.append(Integer.toHexString(hash)).append("|-"); + int[] parentIndices = commit.getParentIndices(); + for (int j = 0 ; j < parentIndices.length; j++) { + if (j > 0) + s.append(" "); + s.append(Integer.toHexString(parentIndices[j])); + } + } + return s.toString(); + } + + public static String permanentGraphToHashIndex(PermanentGraph graph) { + StringBuilder s = new StringBuilder(); + for (int nodeIndex = 0; nodeIndex < graph.nodesCount(); nodeIndex++) { + if (nodeIndex != 0) + s.append("\n"); + + s.append(Integer.toHexString(graph.getHashIndex(nodeIndex))); + } + return s.toString(); + } + + public static String permanentGraphLayoutModelToStr(PermanentGraphLayout graphLayout, int nodesCount) { + StringBuilder s = new StringBuilder(); + for (int nodeIndex = 0; nodeIndex < nodesCount; nodeIndex++) { + if (nodeIndex != 0) + s.append("\n"); + + s.append(graphLayout.getLayoutIndex(nodeIndex)).append("|-").append(graphLayout.getOneOfHeadNodeIndex(nodeIndex)); + } + return s.toString(); + } + + public static String containingBranchesGetterToStr(ContainingBranchesGetter containingBranchesGetter, int nodesCount) { + StringBuilder s = new StringBuilder(); + for (int nodeIndex = 0; nodeIndex < nodesCount; nodeIndex++) { + if (nodeIndex != 0) + s.append("\n"); + + List branchHashIndexes = new ArrayList(containingBranchesGetter.getBranchHashIndexes(nodeIndex)); + if (branchHashIndexes.isEmpty()) { + s.append("none"); + continue; + } + + Collections.sort(branchHashIndexes); + boolean first = true; + for (int hashIndex : branchHashIndexes) { + if (first) + first = false; + else + s.append(" "); + + s.append(Integer.toHexString(hashIndex)); + } + } + return s.toString(); + } +} diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/ContainingBranchesTest.java b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/ContainingBranchesTest.java new file mode 100644 index 000000000000..b53717947fd5 --- /dev/null +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/ContainingBranchesTest.java @@ -0,0 +1,92 @@ +/* + * 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.vcs.log.newgraph.impl; + +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.newgraph.AbstractTestWithTextFile; +import com.intellij.vcs.log.newgraph.GraphFlags; +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.facade.ContainingBranchesGetter; +import com.intellij.vcs.log.newgraph.utils.DfsUtil; +import com.intellij.vcs.log.parser.SimpleCommitListParser; +import org.junit.Test; + +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.intellij.vcs.log.newgraph.GraphStrUtils.containingBranchesGetterToStr; +import static org.junit.Assert.assertEquals; + +public class ContainingBranchesTest extends AbstractTestWithTextFile { + private final static String SEPARATOR = "\nBRANCH NODES:\n"; + + public ContainingBranchesTest() { + super("containingBranches/"); + } + + private static Set parseBranchNodeIndex(String str) { + Set result = new HashSet(); + for (String subStr : str.split("\\s")) { + result.add(Integer.parseInt(subStr)); + } + return result; + } + + + @Override + protected void runTest(String in, String out) { + int i = in.indexOf(SEPARATOR); + List commits = SimpleCommitListParser.parseCommitList(in.substring(0, i)); + + GraphFlags flags = new GraphFlags(commits.size()); + PermanentGraph graph = PermanentGraphBuilder.build(flags.getSimpleNodeFlags(), commits).first; + ContainingBranchesGetter containingBranchesGetter = new ContainingBranchesGetter(graph, + parseBranchNodeIndex(in.substring(i + SEPARATOR.length())), + new DfsUtil(1000), + new GraphFlags(graph.nodesCount()).getTempFlags()); + + assertEquals(out, containingBranchesGetterToStr(containingBranchesGetter, graph.nodesCount())); + } + + @Test + public void simple() throws IOException { + doTest("simple"); + } + + @Test + public void manyNodes() throws IOException { + doTest("manyNodes"); + } + + @Test + public void notFullGraph() throws IOException { + doTest("notFullGraph"); + } + + @Test + public void oneNode() throws IOException { + doTest("oneNode"); + } + + @Test + public void oneNodeNotFullGraph() throws IOException { + doTest("oneNodeNotFullGraph"); + } + +} diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphBuilderHashIndexTest.java b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphBuilderHashIndexTest.java new file mode 100644 index 000000000000..308b1fb39c6c --- /dev/null +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphBuilderHashIndexTest.java @@ -0,0 +1,72 @@ +/* + * 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.vcs.log.newgraph.impl; + +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.newgraph.AbstractTestWithTextFile; +import com.intellij.vcs.log.newgraph.GraphFlags; +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.parser.SimpleCommitListParser; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; + +import static com.intellij.vcs.log.newgraph.GraphStrUtils.permanentGraphToHashIndex; +import static org.junit.Assert.assertEquals; + +public class GraphBuilderHashIndexTest extends AbstractTestWithTextFile { + + public GraphBuilderHashIndexTest() { + super("graphHashIndex/"); + } + + @Override + protected void runTest(String in, String out) { + List commits = SimpleCommitListParser.parseCommitList(in); + GraphFlags flags = new GraphFlags(commits.size()); + PermanentGraph graph = PermanentGraphBuilder.build(flags.getSimpleNodeFlags(), commits).first; + + assertEquals(out, permanentGraphToHashIndex(graph)); + } + + @Test + public void simple() throws IOException { + doTest("simple"); + } + + @Test + public void manyNodes() throws IOException { + doTest("manyNodes"); + } + + @Test + public void notFullGraph() throws IOException { + doTest("notFullGraph"); + } + + @Test + public void oneNode() throws IOException { + doTest("oneNode"); + } + + @Test + public void oneNodeNotFullGraph() throws IOException { + doTest("oneNodeNotFullGraph"); + } + +} diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphBuilderTest.java b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphBuilderTest.java new file mode 100644 index 000000000000..4fb0585bc13b --- /dev/null +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphBuilderTest.java @@ -0,0 +1,102 @@ +/* + * 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.vcs.log.newgraph.impl; + +import com.intellij.openapi.util.Pair; +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.newgraph.AbstractTestWithTextFile; +import com.intellij.vcs.log.newgraph.GraphFlags; +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.parser.SimpleCommitListParser; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static com.intellij.vcs.log.newgraph.GraphStrUtils.commitsWithNotLoadParentMapToStr; +import static com.intellij.vcs.log.newgraph.GraphStrUtils.permanentGraphTorStr; +import static org.junit.Assert.assertEquals; + +public class GraphBuilderTest extends AbstractTestWithTextFile { + public GraphBuilderTest() { + super("graphBuilder/"); + } + + @Override + protected void runTest(String in, String out) { + List commits = SimpleCommitListParser.parseCommitList(in); + GraphFlags flags = new GraphFlags(commits.size()); + Pair> graphAndCommitsWithNotLoadParent = PermanentGraphBuilder.build(flags.getSimpleNodeFlags(), commits); + Map commitsWithNotLoadParent = graphAndCommitsWithNotLoadParent.second; + PermanentGraph graph = graphAndCommitsWithNotLoadParent.first; + + String actual = permanentGraphTorStr(graph); + if (!commitsWithNotLoadParent.isEmpty()) { + actual += "\nNOT LOAD MAP:\n"; + actual += commitsWithNotLoadParentMapToStr(commitsWithNotLoadParent); + } + assertEquals(out, actual); + } + + + + @Test + public void simple() throws IOException { + doTest("simple"); + } + + @Test + public void manyNodes() throws IOException { + doTest("manyNodes"); + } + + @Test + public void manyUpNodes() throws IOException { + doTest("manyUpNodes"); + } + + @Test + public void manyDownNodes() throws IOException { + doTest("manyDownNodes"); + } + + @Test + public void oneNode() throws IOException { + doTest("oneNode"); + } + + @Test + public void oneNodeNotFullGraph() throws IOException { + doTest("oneNodeNotFullGraph"); + } + + @Test + public void notFullGraph() throws IOException { + doTest("notFullGraph"); + } + + @Test + public void parentsOrder() throws IOException { + doTest("parentsOrder"); + } + + @Test + public void duplicateParents() throws IOException { + doTest("duplicateParents"); + } +} diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphFlagsTest.java b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphFlagsTest.java new file mode 100644 index 000000000000..a4f13c5355ad --- /dev/null +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphFlagsTest.java @@ -0,0 +1,131 @@ +/* + * 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.vcs.log.newgraph.impl; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.vcs.log.newgraph.GraphFlags; +import com.intellij.vcs.log.newgraph.utils.Flags; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class GraphFlagsTest { + + private static String flagsToStr(Flags flags) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < flags.size(); i++) { + if (flags.get(i)) { + s.append("1"); + } else { + s.append("0"); + } + } + return s.toString(); + } + + private static void setFlags(Flags flags, String flagsStr) { + for (int i = 0; i < flagsStr.length(); i++) { + flags.set(i, flagsStr.charAt(i) == '1'); + } + } + + private static class Tester { + private final GraphFlags myGraphFlags; + private String myVisibleFlags; + private String myEdgesFlags; + + public static Tester newTester(int size) { + Tester tester = new Tester(new GraphFlags(size), StringUtil.repeat("0", size), StringUtil.repeat("0", size)); + + tester.testAll(); + + return tester; + } + + private Tester(GraphFlags graphFlags, String visibleFlags, String edgesFlags) { + myGraphFlags = graphFlags; + myVisibleFlags = visibleFlags; + myEdgesFlags = edgesFlags; + } + + private void testAll() { + testVisibleFlags(); + testEdgesFlags(); + } + + private void testVisibleFlags() { + assertEquals(myVisibleFlags, flagsToStr(myGraphFlags.getVisibleNodes())); + } + + private void testEdgesFlags() { + assertEquals(myEdgesFlags, flagsToStr(myGraphFlags.getSimpleNodeFlags())); + } + + public void newVisibleFlags(String visibleFlags) { + myVisibleFlags = visibleFlags; + setFlags(myGraphFlags.getVisibleNodes(), visibleFlags); + + testAll(); + } + + public void newEdgesFlags(String edgesFlags) { + myEdgesFlags = edgesFlags; + setFlags(myGraphFlags.getSimpleNodeFlags(), edgesFlags); + + testAll(); + } + } + + + @Test + public void simpleTest() { + Tester tester = Tester.newTester(5); + + tester.newEdgesFlags("00110"); + + tester.newVisibleFlags("01000"); + + tester.newEdgesFlags("01011"); + } + + @Test + public void test1() { + Tester tester = Tester.newTester(1); + + tester.newEdgesFlags("1"); + tester.newEdgesFlags("1"); + + tester.newEdgesFlags("0"); + tester.newEdgesFlags("0"); + + tester.newEdgesFlags("1"); + tester.newEdgesFlags("0"); + + tester.newEdgesFlags("1"); + tester.newVisibleFlags("1"); + + tester.newEdgesFlags("1"); + tester.newVisibleFlags("0"); + + tester.newEdgesFlags("0"); + tester.newVisibleFlags("1"); + + tester.newEdgesFlags("0"); + tester.newVisibleFlags("0"); + } + +} diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphLayoutBuilderTest.java b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphLayoutBuilderTest.java new file mode 100644 index 000000000000..de71e0b7b820 --- /dev/null +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/impl/GraphLayoutBuilderTest.java @@ -0,0 +1,83 @@ +/* + * 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.vcs.log.newgraph.impl; + +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.newgraph.AbstractTestWithTextFile; +import com.intellij.vcs.log.newgraph.GraphFlags; +import com.intellij.vcs.log.newgraph.PermanentGraph; +import com.intellij.vcs.log.newgraph.PermanentGraphLayout; +import com.intellij.vcs.log.newgraph.utils.DfsUtil; +import com.intellij.vcs.log.parser.SimpleCommitListParser; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; + +import java.io.IOException; +import java.util.Comparator; +import java.util.List; + +import static com.intellij.vcs.log.newgraph.GraphStrUtils.permanentGraphLayoutModelToStr; +import static org.junit.Assert.assertEquals; + +public class GraphLayoutBuilderTest extends AbstractTestWithTextFile { + public GraphLayoutBuilderTest() { + super("layoutBuilder/"); + } + + @Override + protected void runTest(String in, String out) { + List commits = SimpleCommitListParser.parseCommitList(in); + GraphFlags flags = new GraphFlags(commits.size()); + final PermanentGraph graph = PermanentGraphBuilder.build(flags.getSimpleNodeFlags(), commits).first; + + DfsUtil dfsUtil = new DfsUtil(commits.size()); + PermanentGraphLayout graphLayout = PermanentGraphLayoutBuilder.build(dfsUtil, graph, new Comparator() { + @Override + public int compare(@NotNull Integer o1, @NotNull Integer o2) { + Integer hashIndex1 = graph.getHashIndex(o1); + return hashIndex1.compareTo(graph.getHashIndex(o2)); + } + }); + + assertEquals(out, permanentGraphLayoutModelToStr(graphLayout, graph.nodesCount())); + } + + @Test + public void manyNodes() throws IOException { + doTest("manyNodes"); + } + + @Test + public void notFullGraph() throws IOException { + doTest("notFullGraph"); + } + + @Test + public void oneNode() throws IOException { + doTest("oneNode"); + } + + @Test + public void oneNodeNotFullGraph() throws IOException { + doTest("oneNodeNotFullGraph"); + } + + @Test + public void headsOrder() throws IOException { + doTest("headsOrder"); + } +} diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/utils/impl/TreeIntToIntMapTest.java b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/utils/impl/TreeIntToIntMapTest.java new file mode 100644 index 000000000000..8ca493097d37 --- /dev/null +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/newgraph/utils/impl/TreeIntToIntMapTest.java @@ -0,0 +1,193 @@ +/* + * 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.vcs.log.newgraph.utils.impl; + +import com.intellij.util.BooleanFunction; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +public class TreeIntToIntMapTest { + + private static class Tester { + + public static Tester getTest(int longSize, String initVisibility) { + Tester tester = new Tester(longSize, parseSet(initVisibility)); + + assertEquals(initVisibility, tester.mapToString()); + return tester; + } + + private static Set parseSet(String visibility) { + Set visibleNodes = new HashSet(); + if (visibility.length() == 0) + return visibleNodes; + + for (String number : visibility.split("\\|")) { + visibleNodes.add(Integer.decode(number)); + } + return visibleNodes; + } + + private final Set myVisibleNodes; + private final TreeIntToIntMap myTreeIntToIntMap; + + private Tester(int longSize, Set visibleNodes) { + myVisibleNodes = visibleNodes; + myTreeIntToIntMap = TreeIntToIntMap.newInstance(new BooleanFunction() { + @Override + public boolean fun(Integer integer) { + return myVisibleNodes.contains(integer); + } + }, longSize); + } + + public void changeVisibility(int updateFrom, int updateTo, String newVisibility) { + myVisibleNodes.clear(); + myVisibleNodes.addAll(parseSet(newVisibility)); + + myTreeIntToIntMap.update(updateFrom, updateTo); + + assertEquals(newVisibility, mapToString()); + } + + public String mapToString() { + StringBuilder s = new StringBuilder(); + for (int shortIndex = 0; shortIndex < myTreeIntToIntMap.shortSize(); shortIndex++) { + if (shortIndex != 0) + s.append("|"); + + s.append(myTreeIntToIntMap.getLongIndex(shortIndex)); + } + return s.toString(); + } + + public String reverseMapToString() { + StringBuilder s = new StringBuilder(); + for (int longIndex = 0; longIndex < myTreeIntToIntMap.longSize(); longIndex++) { + if (longIndex != 0) + s.append("|"); + + s.append(myTreeIntToIntMap.getShortIndex(longIndex)); + } + return s.toString(); + } + + + public void testLongToShort(String expected) { + assertEquals(expected, reverseMapToString()); + } + } + + + @Test + public void simpleTest() { + Tester tester = Tester.getTest(6, "0|1|2|3|4|5"); + + tester.changeVisibility(1, 3, "0|2|4|5"); + tester.changeVisibility(2, 2, "0|4|5"); + tester.changeVisibility(1, 1, "0|1|4|5"); + } + + @Test + public void testOneNode() { + Tester tester = Tester.getTest(1, "0"); + + tester.changeVisibility(0, 0, ""); + tester.changeVisibility(0, 0, ""); + tester.changeVisibility(0, 0, "0"); + tester.changeVisibility(0, 0, "0"); + tester.changeVisibility(0, 0, ""); + } + + @Test + public void testTwoNodes() { + Tester tester = Tester.getTest(2, "0"); + + tester.changeVisibility(1, 1, "0|1"); + tester.changeVisibility(0, 1, ""); + tester.changeVisibility(0, 0, "0"); + } + + @Test + public void test4Nodes() { + Tester tester = Tester.getTest(4, "2|3"); + + tester.changeVisibility(1, 3, "1"); + tester.changeVisibility(0, 2, "0|1|2"); + tester.changeVisibility(0, 0, "1|2"); + } + + @Test + public void test5Nodes() { + Tester tester = Tester.getTest(5, "0|1|2|3|4"); + + tester.changeVisibility(4, 4, "0|1|2|3"); + tester.changeVisibility(3, 4, "0|1|2|4"); + } + + @Test + public void testReverseMap() { + Tester tester = Tester.getTest(7, "0|1|2|3|4|5|6"); + + tester.testLongToShort("0|1|2|3|4|5|6"); + + tester.changeVisibility(2, 4, "0|1|5|6"); + + tester.testLongToShort("0|1|1|1|1|2|3"); + } + + @Test + public void testReverseMap2() { + Tester tester = Tester.getTest(8, "0|4|7"); + + tester.testLongToShort("0|0|0|0|1|1|1|2"); + + tester.changeVisibility(0, 0, "4|7"); + + tester.testLongToShort("0|0|0|0|0|0|0|1"); + } + + @Test + public void testReverseWithMinNodes() { + Tester tester = Tester.getTest(1, ""); + + tester.testLongToShort("0"); + + tester.changeVisibility(0, 0, "0"); + + tester.testLongToShort("0"); + } + + @Test + public void testReverseWithMinNodes2() { + Tester tester = Tester.getTest(2, ""); + + tester.testLongToShort("0|0"); + + tester.changeVisibility(1, 1, "1"); + + tester.testLongToShort("0|0"); + + tester.changeVisibility(0, 0, "0|1"); + + tester.testLongToShort("0|1"); + } +} diff --git a/platform/vcs-log/graph/testData/containingBranches/manyNodes_in.txt b/platform/vcs-log/graph/testData/containingBranches/manyNodes_in.txt new file mode 100644 index 000000000000..ad1af1d04446 --- /dev/null +++ b/platform/vcs-log/graph/testData/containingBranches/manyNodes_in.txt @@ -0,0 +1,10 @@ +a0|-a1 a6 a3 +a1|-a6 +a2|-a3 +a3|- +a4|-a5 +a5|- +a6|-a7 +a7|- +BRANCH NODES: +1 3 7 4 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/containingBranches/manyNodes_out.txt b/platform/vcs-log/graph/testData/containingBranches/manyNodes_out.txt new file mode 100644 index 000000000000..e081286d94e8 --- /dev/null +++ b/platform/vcs-log/graph/testData/containingBranches/manyNodes_out.txt @@ -0,0 +1,8 @@ +none +a1 +none +a3 +a4 +a4 +a1 +a1 a7 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/containingBranches/notFullGraph_in.txt b/platform/vcs-log/graph/testData/containingBranches/notFullGraph_in.txt new file mode 100644 index 000000000000..e9244013cc9a --- /dev/null +++ b/platform/vcs-log/graph/testData/containingBranches/notFullGraph_in.txt @@ -0,0 +1,7 @@ +a0|-a1 a6 a3 +a1|-a6 +a2|-a3 +a3|- +a4|-a5 +BRANCH NODES: +0 2 5 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/containingBranches/notFullGraph_out.txt b/platform/vcs-log/graph/testData/containingBranches/notFullGraph_out.txt new file mode 100644 index 000000000000..11eade954283 --- /dev/null +++ b/platform/vcs-log/graph/testData/containingBranches/notFullGraph_out.txt @@ -0,0 +1,5 @@ +a0 +a0 +a2 +a0 a2 +none \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/containingBranches/oneNodeNotFullGraph_in.txt b/platform/vcs-log/graph/testData/containingBranches/oneNodeNotFullGraph_in.txt new file mode 100644 index 000000000000..d22e178acb29 --- /dev/null +++ b/platform/vcs-log/graph/testData/containingBranches/oneNodeNotFullGraph_in.txt @@ -0,0 +1,3 @@ +a0|-a1 a3 a5 +BRANCH NODES: +100 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/containingBranches/oneNodeNotFullGraph_out.txt b/platform/vcs-log/graph/testData/containingBranches/oneNodeNotFullGraph_out.txt new file mode 100644 index 000000000000..c86c3f3551ec --- /dev/null +++ b/platform/vcs-log/graph/testData/containingBranches/oneNodeNotFullGraph_out.txt @@ -0,0 +1 @@ +none \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/containingBranches/oneNode_in.txt b/platform/vcs-log/graph/testData/containingBranches/oneNode_in.txt new file mode 100644 index 000000000000..36b02036de38 --- /dev/null +++ b/platform/vcs-log/graph/testData/containingBranches/oneNode_in.txt @@ -0,0 +1,3 @@ +a0|- +BRANCH NODES: +0 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/containingBranches/oneNode_out.txt b/platform/vcs-log/graph/testData/containingBranches/oneNode_out.txt new file mode 100644 index 000000000000..c545f2f98139 --- /dev/null +++ b/platform/vcs-log/graph/testData/containingBranches/oneNode_out.txt @@ -0,0 +1 @@ +a0 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/containingBranches/simple_in.txt b/platform/vcs-log/graph/testData/containingBranches/simple_in.txt new file mode 100644 index 000000000000..f5613657b6f3 --- /dev/null +++ b/platform/vcs-log/graph/testData/containingBranches/simple_in.txt @@ -0,0 +1,6 @@ +a0|-a1 +a1|-a2 +a2|-a3 +a3|- +BRANCH NODES: +0 2 3 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/containingBranches/simple_out.txt b/platform/vcs-log/graph/testData/containingBranches/simple_out.txt new file mode 100644 index 000000000000..0280cffaddc6 --- /dev/null +++ b/platform/vcs-log/graph/testData/containingBranches/simple_out.txt @@ -0,0 +1,4 @@ +a0 +a0 +a0 a2 +a0 a2 a3 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/duplicateParents_in.txt b/platform/vcs-log/graph/testData/graphBuilder/duplicateParents_in.txt new file mode 100644 index 000000000000..29c7a0323d27 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/duplicateParents_in.txt @@ -0,0 +1,7 @@ +0|-1 1 1 3 1 1 +1|-6 +2|-3 4 4 3 +3|-4 4 +4|-5 6 +5|- +6|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/duplicateParents_out.txt b/platform/vcs-log/graph/testData/graphBuilder/duplicateParents_out.txt new file mode 100644 index 000000000000..15bb5f07ed6f --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/duplicateParents_out.txt @@ -0,0 +1,7 @@ +0|-|-1 3 +1|-0|-6 +2|-|-3 4 +3|-0 2|-4 +4|-2 3|-5 6 +5|-4|- +6|-1 4|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/manyDownNodes_in.txt b/platform/vcs-log/graph/testData/graphBuilder/manyDownNodes_in.txt new file mode 100644 index 000000000000..9349b9ba3817 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/manyDownNodes_in.txt @@ -0,0 +1,5 @@ +a0|-a1 a2 a3 a4 +a1|- +a2|- +a3|- +a4|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/manyDownNodes_out.txt b/platform/vcs-log/graph/testData/graphBuilder/manyDownNodes_out.txt new file mode 100644 index 000000000000..fccd5e26cbbf --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/manyDownNodes_out.txt @@ -0,0 +1,5 @@ +0|-|-1 2 3 4 +1|-0|- +2|-0|- +3|-0|- +4|-0|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/manyNodes_in.txt b/platform/vcs-log/graph/testData/graphBuilder/manyNodes_in.txt new file mode 100644 index 000000000000..d3f734de199f --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/manyNodes_in.txt @@ -0,0 +1,8 @@ +a0|-a1 a6 a3 +a1|-a6 +a2|-a3 +a3|- +a4|-a5 +a5|- +a6|-a7 +a7|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/manyNodes_out.txt b/platform/vcs-log/graph/testData/graphBuilder/manyNodes_out.txt new file mode 100644 index 000000000000..9eee1e6cac6c --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/manyNodes_out.txt @@ -0,0 +1,8 @@ +0|-|-1 6 3 +1|-0|-6 +2|-|-3 +3|-0 2|- +4|-|-5 +5|-4|- +6|-0 1|-7 +7|-6|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/manyUpNodes_in.txt b/platform/vcs-log/graph/testData/graphBuilder/manyUpNodes_in.txt new file mode 100644 index 000000000000..a062d3b648dd --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/manyUpNodes_in.txt @@ -0,0 +1,5 @@ +a0|-a4 +a1|-a4 +a2|-a4 +a3|-a4 +a4|- diff --git a/platform/vcs-log/graph/testData/graphBuilder/manyUpNodes_out.txt b/platform/vcs-log/graph/testData/graphBuilder/manyUpNodes_out.txt new file mode 100644 index 000000000000..57a7781e48d6 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/manyUpNodes_out.txt @@ -0,0 +1,5 @@ +0|-|-4 +1|-|-4 +2|-|-4 +3|-|-4 +4|-0 1 2 3|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/notFullGraph_in.txt b/platform/vcs-log/graph/testData/graphBuilder/notFullGraph_in.txt new file mode 100644 index 000000000000..7943d8c1ffb0 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/notFullGraph_in.txt @@ -0,0 +1,5 @@ +a0|-a1 a6 a3 +a1|-a6 +a2|-a3 +a3|- +a4|-a5 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/notFullGraph_out.txt b/platform/vcs-log/graph/testData/graphBuilder/notFullGraph_out.txt new file mode 100644 index 000000000000..262c3a5612b4 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/notFullGraph_out.txt @@ -0,0 +1,9 @@ +0|-|-1 2147483647 3 +1|-0|-2147483647 +2|-|-3 +3|-0 2|- +4|-|-2147483647 +NOT LOAD MAP: +a0|-a1 a6 a3 +a1|-a6 +a4|-a5 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/oneNodeNotFullGraph_in.txt b/platform/vcs-log/graph/testData/graphBuilder/oneNodeNotFullGraph_in.txt new file mode 100644 index 000000000000..be2a1e0b6a32 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/oneNodeNotFullGraph_in.txt @@ -0,0 +1 @@ +a0|-a1 a3 a5 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/oneNodeNotFullGraph_out.txt b/platform/vcs-log/graph/testData/graphBuilder/oneNodeNotFullGraph_out.txt new file mode 100644 index 000000000000..e2b4b6571a5c --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/oneNodeNotFullGraph_out.txt @@ -0,0 +1,3 @@ +0|-|-2147483647 2147483647 2147483647 +NOT LOAD MAP: +a0|-a1 a3 a5 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/oneNode_in.txt b/platform/vcs-log/graph/testData/graphBuilder/oneNode_in.txt new file mode 100644 index 000000000000..767a94f56fa1 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/oneNode_in.txt @@ -0,0 +1 @@ +a0|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/oneNode_out.txt b/platform/vcs-log/graph/testData/graphBuilder/oneNode_out.txt new file mode 100644 index 000000000000..782e9fa53fce --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/oneNode_out.txt @@ -0,0 +1 @@ +0|-|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/parentsOrder_in.txt b/platform/vcs-log/graph/testData/graphBuilder/parentsOrder_in.txt new file mode 100644 index 000000000000..76d9f99370cf --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/parentsOrder_in.txt @@ -0,0 +1,6 @@ +a66|-ffff a aa +aa|-f ffff ff +ffff|-ff +ff|- +a|- +f|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/parentsOrder_out.txt b/platform/vcs-log/graph/testData/graphBuilder/parentsOrder_out.txt new file mode 100644 index 000000000000..2f65bac78c07 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/parentsOrder_out.txt @@ -0,0 +1,6 @@ +0|-|-2 4 1 +1|-0|-5 2 3 +2|-0 1|-3 +3|-1 2|- +4|-0|- +5|-1|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/simple_in.txt b/platform/vcs-log/graph/testData/graphBuilder/simple_in.txt new file mode 100644 index 000000000000..7b8352cba91d --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/simple_in.txt @@ -0,0 +1,4 @@ +a0|-a1 +a1|-a2 +a2|-a3 +a3|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphBuilder/simple_out.txt b/platform/vcs-log/graph/testData/graphBuilder/simple_out.txt new file mode 100644 index 000000000000..08566049d3d9 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphBuilder/simple_out.txt @@ -0,0 +1,4 @@ +0|-|-1 +1|-0|-2 +2|-1|-3 +3|-2|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphHashIndex/manyNodes_in.txt b/platform/vcs-log/graph/testData/graphHashIndex/manyNodes_in.txt new file mode 100644 index 000000000000..d3f734de199f --- /dev/null +++ b/platform/vcs-log/graph/testData/graphHashIndex/manyNodes_in.txt @@ -0,0 +1,8 @@ +a0|-a1 a6 a3 +a1|-a6 +a2|-a3 +a3|- +a4|-a5 +a5|- +a6|-a7 +a7|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphHashIndex/manyNodes_out.txt b/platform/vcs-log/graph/testData/graphHashIndex/manyNodes_out.txt new file mode 100644 index 000000000000..af0037545a99 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphHashIndex/manyNodes_out.txt @@ -0,0 +1,8 @@ +a0 +a1 +a2 +a3 +a4 +a5 +a6 +a7 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphHashIndex/notFullGraph_in.txt b/platform/vcs-log/graph/testData/graphHashIndex/notFullGraph_in.txt new file mode 100644 index 000000000000..7943d8c1ffb0 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphHashIndex/notFullGraph_in.txt @@ -0,0 +1,5 @@ +a0|-a1 a6 a3 +a1|-a6 +a2|-a3 +a3|- +a4|-a5 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphHashIndex/notFullGraph_out.txt b/platform/vcs-log/graph/testData/graphHashIndex/notFullGraph_out.txt new file mode 100644 index 000000000000..3e24c221ff0a --- /dev/null +++ b/platform/vcs-log/graph/testData/graphHashIndex/notFullGraph_out.txt @@ -0,0 +1,5 @@ +a0 +a1 +a2 +a3 +a4 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphHashIndex/oneNodeNotFullGraph_in.txt b/platform/vcs-log/graph/testData/graphHashIndex/oneNodeNotFullGraph_in.txt new file mode 100644 index 000000000000..be2a1e0b6a32 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphHashIndex/oneNodeNotFullGraph_in.txt @@ -0,0 +1 @@ +a0|-a1 a3 a5 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphHashIndex/oneNodeNotFullGraph_out.txt b/platform/vcs-log/graph/testData/graphHashIndex/oneNodeNotFullGraph_out.txt new file mode 100644 index 000000000000..c545f2f98139 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphHashIndex/oneNodeNotFullGraph_out.txt @@ -0,0 +1 @@ +a0 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphHashIndex/oneNode_in.txt b/platform/vcs-log/graph/testData/graphHashIndex/oneNode_in.txt new file mode 100644 index 000000000000..767a94f56fa1 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphHashIndex/oneNode_in.txt @@ -0,0 +1 @@ +a0|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphHashIndex/oneNode_out.txt b/platform/vcs-log/graph/testData/graphHashIndex/oneNode_out.txt new file mode 100644 index 000000000000..c545f2f98139 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphHashIndex/oneNode_out.txt @@ -0,0 +1 @@ +a0 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphHashIndex/simple_in.txt b/platform/vcs-log/graph/testData/graphHashIndex/simple_in.txt new file mode 100644 index 000000000000..ac8ce56945b2 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphHashIndex/simple_in.txt @@ -0,0 +1,5 @@ +aff|- +23ab|- +1adf|- +21|- +ffffff|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/graphHashIndex/simple_out.txt b/platform/vcs-log/graph/testData/graphHashIndex/simple_out.txt new file mode 100644 index 000000000000..c1f0269958b2 --- /dev/null +++ b/platform/vcs-log/graph/testData/graphHashIndex/simple_out.txt @@ -0,0 +1,5 @@ +aff +23ab +1adf +21 +ffffff \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/layoutBuilder/headsOrder_in.txt b/platform/vcs-log/graph/testData/layoutBuilder/headsOrder_in.txt new file mode 100644 index 000000000000..e436f932fad7 --- /dev/null +++ b/platform/vcs-log/graph/testData/layoutBuilder/headsOrder_in.txt @@ -0,0 +1,5 @@ +ff|-a1 a3 +a1|-a4 +a2|-a3 +a3|-a4 +a4|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/layoutBuilder/headsOrder_out.txt b/platform/vcs-log/graph/testData/layoutBuilder/headsOrder_out.txt new file mode 100644 index 000000000000..c61f7239ca64 --- /dev/null +++ b/platform/vcs-log/graph/testData/layoutBuilder/headsOrder_out.txt @@ -0,0 +1,5 @@ +2|-0 +2|-0 +1|-2 +1|-2 +1|-2 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/layoutBuilder/manyNodes_in.txt b/platform/vcs-log/graph/testData/layoutBuilder/manyNodes_in.txt new file mode 100644 index 000000000000..8982fbb2376e --- /dev/null +++ b/platform/vcs-log/graph/testData/layoutBuilder/manyNodes_in.txt @@ -0,0 +1,9 @@ +a0|-a1 a6 a3 +a1|-a7 +a2|-a3 +a3|- +a4|-a5 +a5|- +a6|-a7 +a7|-a8 +a8|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/layoutBuilder/manyNodes_out.txt b/platform/vcs-log/graph/testData/layoutBuilder/manyNodes_out.txt new file mode 100644 index 000000000000..415b3e125d37 --- /dev/null +++ b/platform/vcs-log/graph/testData/layoutBuilder/manyNodes_out.txt @@ -0,0 +1,9 @@ +1|-0 +1|-0 +4|-2 +3|-0 +5|-4 +5|-4 +2|-0 +1|-0 +1|-0 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/layoutBuilder/notFullGraph_in.txt b/platform/vcs-log/graph/testData/layoutBuilder/notFullGraph_in.txt new file mode 100644 index 000000000000..7943d8c1ffb0 --- /dev/null +++ b/platform/vcs-log/graph/testData/layoutBuilder/notFullGraph_in.txt @@ -0,0 +1,5 @@ +a0|-a1 a6 a3 +a1|-a6 +a2|-a3 +a3|- +a4|-a5 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/layoutBuilder/notFullGraph_out.txt b/platform/vcs-log/graph/testData/layoutBuilder/notFullGraph_out.txt new file mode 100644 index 000000000000..bc40e6aa16b7 --- /dev/null +++ b/platform/vcs-log/graph/testData/layoutBuilder/notFullGraph_out.txt @@ -0,0 +1,5 @@ +1|-0 +1|-0 +3|-2 +2|-0 +4|-4 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/layoutBuilder/oneNodeNotFullGraph_in.txt b/platform/vcs-log/graph/testData/layoutBuilder/oneNodeNotFullGraph_in.txt new file mode 100644 index 000000000000..be2a1e0b6a32 --- /dev/null +++ b/platform/vcs-log/graph/testData/layoutBuilder/oneNodeNotFullGraph_in.txt @@ -0,0 +1 @@ +a0|-a1 a3 a5 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/layoutBuilder/oneNodeNotFullGraph_out.txt b/platform/vcs-log/graph/testData/layoutBuilder/oneNodeNotFullGraph_out.txt new file mode 100644 index 000000000000..9010cabcebfb --- /dev/null +++ b/platform/vcs-log/graph/testData/layoutBuilder/oneNodeNotFullGraph_out.txt @@ -0,0 +1 @@ +1|-0 \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/layoutBuilder/oneNode_in.txt b/platform/vcs-log/graph/testData/layoutBuilder/oneNode_in.txt new file mode 100644 index 000000000000..767a94f56fa1 --- /dev/null +++ b/platform/vcs-log/graph/testData/layoutBuilder/oneNode_in.txt @@ -0,0 +1 @@ +a0|- \ No newline at end of file diff --git a/platform/vcs-log/graph/testData/layoutBuilder/oneNode_out.txt b/platform/vcs-log/graph/testData/layoutBuilder/oneNode_out.txt new file mode 100644 index 000000000000..9010cabcebfb --- /dev/null +++ b/platform/vcs-log/graph/testData/layoutBuilder/oneNode_out.txt @@ -0,0 +1 @@ +1|-0 \ No newline at end of file diff --git a/platform/vcs-log/graph/vcs-log-graph.iml b/platform/vcs-log/graph/vcs-log-graph.iml index f3e768b40041..ebacf13cf84a 100644 --- a/platform/vcs-log/graph/vcs-log-graph.iml +++ b/platform/vcs-log/graph/vcs-log-graph.iml @@ -5,12 +5,11 @@ + - - diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataGetter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataGetter.java index 074dc2ebaa8b..f410c72fbf27 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataGetter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataGetter.java @@ -81,7 +81,7 @@ public abstract class DataGetter implements Dis } @Nullable - public T getCommitData(int row, @NotNull AbstractVcsLogTableModel tableModel) { + public T getCommitData(int row, @NotNull AbstractVcsLogTableModel tableModel) { assert EventQueue.isDispatchThread(); Hash hash = tableModel.getHashAtRow(row); if (hash == null) { @@ -116,7 +116,7 @@ public abstract class DataGetter implements Dis return (T)myDataHolder.getTopCommitDetails(hash); } - private void runLoadAroundCommitData(int row, @NotNull AbstractVcsLogTableModel tableModel) { + private void runLoadAroundCommitData(int row, @NotNull AbstractVcsLogTableModel tableModel) { long taskNumber = myCurrentTaskIndex++; MultiMap commits = getCommitsAround(row, tableModel, UP_PRELOAD_COUNT, DOWN_PRELOAD_COUNT); for (Map.Entry> hashesByRoots : commits.entrySet()) { @@ -138,7 +138,7 @@ public abstract class DataGetter implements Dis @NotNull private static MultiMap getCommitsAround(int selectedRow, - @NotNull AbstractVcsLogTableModel model, + @NotNull AbstractVcsLogTableModel model, int above, int below) { MultiMap commits = MultiMap.create(); for (int row = Math.max(0, selectedRow - above); row < selectedRow + below && row < model.getRowCount(); row++) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java index 3ddb036ef802..867431853d87 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java @@ -1,80 +1,75 @@ package com.intellij.vcs.log.data; import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.util.Consumer; -import com.intellij.util.Function; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.NotNullFunction; -import com.intellij.util.containers.Predicate; -import com.intellij.vcs.log.GraphCommit; -import com.intellij.vcs.log.Hash; -import com.intellij.vcs.log.VcsRef; -import com.intellij.vcs.log.compressedlist.UpdateRequest; -import com.intellij.vcs.log.graph.Graph; -import com.intellij.vcs.log.graph.elements.Edge; -import com.intellij.vcs.log.graph.elements.GraphElement; -import com.intellij.vcs.log.graph.elements.Node; -import com.intellij.vcs.log.graph.mutable.GraphBuilder; -import com.intellij.vcs.log.graph.mutable.MutableGraph; -import com.intellij.vcs.log.graphmodel.GraphModel; -import com.intellij.vcs.log.graphmodel.impl.GraphModelImpl; -import com.intellij.vcs.log.printmodel.GraphPrintCellModel; -import com.intellij.vcs.log.printmodel.impl.GraphPrintCellModelImpl; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.*; +import com.intellij.vcs.log.graph.*; +import com.intellij.vcs.log.newgraph.facade.GraphFacadeImpl; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.List; -/** - * @author erokhins - */ public class DataPack { + private static final boolean USE_NEW_FACADE = true; + - @NotNull private final GraphModel myGraphModel; @NotNull private final RefsModel myRefsModel; - @NotNull private final GraphPrintCellModel myPrintCellModel; - private final NotNullFunction myHashGetter; - private final NotNullFunction myIndexGetter; + @NotNull private final GraphFacade myGraphFacade; @NotNull - public static DataPack build(@NotNull List commits, @NotNull Collection allRefs, @NotNull ProgressIndicator indicator, - NotNullFunction hashGetter, NotNullFunction indexGetter) { + public static DataPack build(@NotNull List commits, + @NotNull Collection allRefs, + @NotNull ProgressIndicator indicator, + @NotNull NotNullFunction indexGetter, + @NotNull NotNullFunction hashGetter, + @NotNull Map logProviders) { indicator.setText("Building graph..."); - - MutableGraph graph = GraphBuilder.build(commits, allRefs); - - GraphModel graphModel = new GraphModelImpl(graph); - - final GraphPrintCellModel printCellModel = new GraphPrintCellModelImpl(graphModel.getGraph()); - graphModel.addUpdateListener(new Consumer() { - @Override - public void consume(UpdateRequest key) { - printCellModel.recalculate(key); - } - }); - final RefsModel refsModel = new RefsModel(allRefs, indexGetter); - graphModel.getFragmentManager().setUnconcealedNodeFunction(new Function() { - @NotNull - @Override - public Boolean fun(@NotNull Node key) { - if (key.getDownEdges().isEmpty() || key.getUpEdges().isEmpty() || refsModel.isBranchRef(key.getCommitIndex())) { - return true; - } - else { - return false; - } + GraphColorManagerImpl colorManager = new GraphColorManagerImpl(refsModel, hashGetter, getRefManagerMap(logProviders)); + if (USE_NEW_FACADE) { + if (!commits.isEmpty()) { + return new DataPack(refsModel, GraphFacadeImpl.newInstance(commits, getBranchCommitHashIndexes(allRefs, indexGetter), colorManager)); } - }); - return new DataPack(graphModel, refsModel, printCellModel, hashGetter, indexGetter); + else { + return new DataPack(refsModel, new EmptyGraphFacade()); + } + } else { + GraphFacade graphFacade = new GraphFacadeBuilderImpl().build(commits, refsModel, colorManager); + return new DataPack(refsModel, graphFacade); + } } - private DataPack(@NotNull GraphModel graphModel, @NotNull RefsModel refsModel, @NotNull GraphPrintCellModel printCellModel, - NotNullFunction hashGetter, NotNullFunction indexGetter) { - myGraphModel = graphModel; + @NotNull + private static Set getBranchCommitHashIndexes(@NotNull Collection allRefs, + @NotNull NotNullFunction indexGetter) { + Set result = new HashSet(); + for (VcsRef vcsRef : allRefs) { + if (vcsRef.getType().isBranch()) + result.add(indexGetter.fun(vcsRef.getCommitHash())); + } + return result; + } + + @NotNull + private static Map getRefManagerMap(@NotNull Map logProviders) { + Map map = ContainerUtil.newHashMap(); + for (Map.Entry entry : logProviders.entrySet()) { + map.put(entry.getKey(), entry.getValue().getReferenceManager()); + } + return map; + } + + private DataPack(@NotNull RefsModel refsModel, @NotNull GraphFacade graphFacade) { myRefsModel = refsModel; - myPrintCellModel = printCellModel; - myHashGetter = hashGetter; - myIndexGetter = indexGetter; + myGraphFacade = graphFacade; + } + + @NotNull + public GraphFacade getGraphFacade() { + return myGraphFacade; } @NotNull @@ -82,132 +77,4 @@ public class DataPack { return myRefsModel; } - @NotNull - public GraphModel getGraphModel() { - return myGraphModel; - } - - @NotNull - public GraphPrintCellModel getPrintCellModel() { - return myPrintCellModel; - } - - public boolean isSameBranch(@NotNull Node nodeA, @NotNull Node nodeB) { - Node up, down; - if (nodeA.getRowIndex() > nodeB.getRowIndex()) { - up = nodeB; - down = nodeA; - } else { - up = nodeA; - down = nodeB; - } - return getGraphModel().getFragmentManager().getUpNodes(down).contains(up); - } - - @NotNull - public Set getUpRefNodes(@NotNull GraphElement graphElement) { - Set nodes = new HashSet(); - for (Node node : getGraphModel().getFragmentManager().getUpNodes(graphElement)) { - if (getRefsModel().isBranchRef(node.getCommitIndex())) { - nodes.add(node); - } - } - return nodes; - } - - @Nullable - public Node getNode(int rowIndex) { - return getGraphModel().getGraph().getCommitNodeInRow(rowIndex); - } - - public int getRowByHash(Hash commitHash) { - Node node = getNodeByHash(commitHash); - return node == null ? -1 : node.getRowIndex(); - } - - @Nullable - public Node getNodeByHash(Hash hash) { - int index = myIndexGetter.fun(hash); - Graph graph = getGraphModel().getGraph(); - for (int i = 0; i < graph.getNodeRows().size(); i++) { - Node node = graph.getCommitNodeInRow(i); - if (node != null && node.getCommitIndex() == index) { - return node; - } - } - return null; - } - - @Nullable - public Node getNodeByPartOfHash(@NotNull String hash) { - Graph graph = getGraphModel().getGraph(); - for (int i = 0; i < graph.getNodeRows().size(); i++) { - Node node = graph.getCommitNodeInRow(i); - if (node != null && myHashGetter.fun(node.getCommitIndex()).asString().startsWith(hash.toLowerCase())) { - return node; - } - } - return null; - } - - @Nullable - public Node getCommonParent(Node a, Node b) { - List commitDiff = getCommitsDownToCommon(a, b); - return commitDiff.isEmpty() ? null : commitDiff.get(commitDiff.size() - 1); - } - - public boolean isAncestorOf(Node ancestor, Node child) { - return ancestor != child && getGraphModel().getFragmentManager().getUpNodes(ancestor).contains(child); - } - - @Nullable - public Node getCommonParent(Node a, Node b, Node c) { - return null; - } - - public List getCommitsDownToCommon(Node newBase, Node head) { - final List all = getAllAncestors(newBase, new Predicate() { - @Override - public boolean apply(@Nullable Node input) { - return false; - } - }); - return getAllAncestors(head, new Predicate() { - @Override - public boolean apply(@Nullable Node input) { - return all.contains(input); - } - }); - } - - private List getAllAncestors(Node a, Predicate stop) { - Set all = new LinkedHashSet(); - Queue queue = new ArrayDeque(); - queue.add(a); - while (!queue.isEmpty()) { - Node aNode = queue.remove(); - all.add(aNode); - if (stop.apply(aNode)) { - return new ArrayList(all); - } - for (Edge edge : aNode.getDownEdges()) { - queue.add(edge.getDownNode()); - } - } - return new ArrayList(all); - } - - public List getCommitsInBranchAboveBase(Node base, Node branchHead) { - List result = new ArrayList(); - Node node = branchHead; - while (node != base) { - result.add(node); - // TODO: multiple edges must not appear - // TODO: if there are no edges, we are in the wrong branch - node = node.getDownEdges().get(0).getDownNode(); - } - //Collections.reverse(result); - return result; - } - } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyGraphFacade.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyGraphFacade.java new file mode 100644 index 000000000000..f1419ed543a5 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyGraphFacade.java @@ -0,0 +1,109 @@ +/* + * 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.vcs.log.data; + +import com.intellij.openapi.util.Condition; +import com.intellij.util.ui.UIUtil; +import com.intellij.vcs.log.graph.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +class EmptyGraphFacade implements GraphFacade { + + public static final BufferedImage EMPTY_IMAGE = UIUtil.createImage(1, 1, Transparency.TRANSLUCENT); + + @NotNull + @Override + public PaintInfo paint(int visibleRow) { + return new PaintInfo() { + @NotNull + @Override + public Image getImage() { + return EMPTY_IMAGE; + } + + @Override + public int getWidth() { + return 0; + } + }; + } + + @Nullable + @Override + public GraphAnswer performAction(@NotNull GraphAction action) { + return null; + } + + @NotNull + @Override + public List getAllCommits() { + return Collections.emptyList(); + } + + @Override + public int getCommitAtRow(int visibleRow) { + return -1; + } + + @Override + public int getVisibleCommitCount() { + return 0; + } + + @Override + public void setVisibleBranches(@Nullable Collection heads) { + } + + @Override + public void setFilter(@Nullable Condition visibilityPredicate) { + } + + @NotNull + @Override + public GraphInfoProvider getInfoProvider() { + return new GraphInfoProvider() { + @NotNull + @Override + public Set getContainingBranches(int visibleRow) { + return Collections.emptySet(); + } + + @NotNull + @Override + public RowInfo getRowInfo(int visibleRow) { + return new RowInfo() { + @Override + public int getOneOfHeads() { + return -1; + } + }; + } + + @Override + public boolean areLongEdgesHidden() { + return false; + } + }; + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/GraphFacadeBuilder.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/GraphFacadeBuilder.java new file mode 100644 index 000000000000..5b0f505a787b --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/GraphFacadeBuilder.java @@ -0,0 +1,30 @@ +/* + * 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.vcs.log.data; + +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.graph.GraphColorManagerImpl; +import com.intellij.vcs.log.graph.GraphFacade; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface GraphFacadeBuilder { + + @NotNull + GraphFacade build(@NotNull List commits, @NotNull RefsModel allRefs, @NotNull GraphColorManagerImpl colorManager); + +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/GraphFacadeBuilderImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/GraphFacadeBuilderImpl.java new file mode 100644 index 000000000000..7d3007f30158 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/GraphFacadeBuilderImpl.java @@ -0,0 +1,68 @@ +/* + * 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.vcs.log.data; + +import com.intellij.util.Consumer; +import com.intellij.util.Function; +import com.intellij.vcs.log.GraphCommit; +import com.intellij.vcs.log.compressedlist.UpdateRequest; +import com.intellij.vcs.log.graph.GraphColorManagerImpl; +import com.intellij.vcs.log.graph.GraphFacade; +import com.intellij.vcs.log.graph.GraphFacadeImpl; +import com.intellij.vcs.log.graph.elements.Node; +import com.intellij.vcs.log.graph.mutable.GraphBuilder; +import com.intellij.vcs.log.graph.mutable.MutableGraph; +import com.intellij.vcs.log.graphmodel.GraphModel; +import com.intellij.vcs.log.graphmodel.impl.GraphModelImpl; +import com.intellij.vcs.log.printmodel.GraphPrintCellModel; +import com.intellij.vcs.log.printmodel.impl.GraphPrintCellModelImpl; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +class GraphFacadeBuilderImpl implements GraphFacadeBuilder { + @NotNull + @Override + public GraphFacade build(@NotNull List commits, + @NotNull final RefsModel refsModel, + @NotNull GraphColorManagerImpl colorManager) { + MutableGraph graph = GraphBuilder.build(commits, refsModel.getAllRefs()); + GraphModel graphModel = new GraphModelImpl(graph); + + final GraphPrintCellModel printCellModel = new GraphPrintCellModelImpl(graphModel.getGraph()); + graphModel.addUpdateListener(new Consumer() { + @Override + public void consume(UpdateRequest key) { + printCellModel.recalculate(key); + } + }); + + graphModel.getFragmentManager().setUnconcealedNodeFunction(new Function() { + @NotNull + @Override + public Boolean fun(@NotNull Node key) { + if (key.getDownEdges().isEmpty() || key.getUpEdges().isEmpty() || refsModel.isBranchRef(key.getCommitIndex())) { + return true; + } + else { + return false; + } + } + }); + + return new GraphFacadeImpl(graphModel, printCellModel, colorManager); + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogBranchFilterImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogBranchFilterImpl.java index 8872406fb88d..f16a44094002 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogBranchFilterImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogBranchFilterImpl.java @@ -3,13 +3,12 @@ package com.intellij.vcs.log.data; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.VcsLogBranchFilter; -import com.intellij.vcs.log.VcsLogGraphFilter; import com.intellij.vcs.log.VcsRef; import org.jetbrains.annotations.NotNull; import java.util.Collection; -public class VcsLogBranchFilterImpl implements VcsLogBranchFilter, VcsLogGraphFilter { +public class VcsLogBranchFilterImpl implements VcsLogBranchFilter { @NotNull private final Collection myMatchingHeads; @NotNull private final Collection myBranchNames; @@ -27,11 +26,6 @@ public class VcsLogBranchFilterImpl implements VcsLogBranchFilter, VcsLogGraphFi }); } - @Override - public boolean matches(int hash) { - return myMatchingHeads.contains(hash); - } - @Override public String toString() { return "on: " + myBranchNames; @@ -43,4 +37,10 @@ public class VcsLogBranchFilterImpl implements VcsLogBranchFilter, VcsLogGraphFi return myBranchNames; } + @Override + @NotNull + public Collection getMatchingHeads() { + return myMatchingHeads; + } + } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java index f5fc5f495173..666ad9f6bb59 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java @@ -17,7 +17,6 @@ package com.intellij.vcs.log.data; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.diagnostic.Attachment; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.BackgroundTaskQueue; import com.intellij.openapi.progress.ProgressIndicator; @@ -35,6 +34,8 @@ import com.intellij.util.containers.HashSet; import com.intellij.util.messages.Topic; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.*; +import com.intellij.vcs.log.impl.TimedVcsCommitImpl; +import com.intellij.vcs.log.util.StopWatch; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,16 +47,13 @@ import java.util.concurrent.atomic.AtomicBoolean; /** *

Holds the commit data loaded from the VCS, and is capable to refresh this data by * {@link VcsLogProvider#subscribeToRootRefreshEvents(Collection, VcsLogRefresher) events from the VCS}.

- *

If refresh is in progress, {@link #getDataPack()} returns the previous data pack (possibly not actual anymore). - * When refresh completes, the data pack instance is updated. Refreshes are chained.

+ *

When refresh is completes, a new {@link DataPack} object is generated, and all UI is updated accordingly.

* *

Thread-safety: the class is thread-safe: *

    *
  • All write-operations made to the log and data pack are made sequentially, from the same background queue;
  • *
  • Once a refresh request is received, we read logs and refs from all providers, join refresh data, join repositories, * and build the new data pack sequentially in a single thread. After that we substitute the instance of the LogData object.
  • - *
  • Whilst we are in the middle of this refresh process, anyone who requests the data pack (and possibly the log if this would - * be available in the future), will get the consistent previous version of it.
  • *

* *

Initialization:

    @@ -87,7 +85,7 @@ import java.util.concurrent.atomic.AtomicBoolean; */ public class VcsLogDataHolder implements Disposable { - public static final Topic REFRESH_COMPLETED = Topic.create("Vcs.Log.Completed", Runnable.class); + public static final Topic REFRESH_COMPLETED = Topic.create("Vcs.Log.Completed", VcsLogRefreshListener.class); private static final Logger LOG = Logger.getInstance(VcsLogDataHolder.class); @@ -209,7 +207,8 @@ public class VcsLogDataHolder implements Disposable { } } - public void initialize(@NotNull final Consumer onInitialized) { + public void initialize(@NotNull final PairConsumer onInitialized) { + final StopWatch initSw = StopWatch.start("initialize"); // complete refresh => other scheduled refreshes are not interesting // TODO: interrupt the current task as well instead of waiting for it to finish, since the result is invalid anyway myDataLoaderQueue.clear(); @@ -225,15 +224,17 @@ public class VcsLogDataHolder implements Disposable { myEntireLogLoadWaiter.countDown(); // make sure to release any potential waiters of the previous latch myEntireLogLoadWaiter = new CountDownLatch(1); - onInitialized.consume(VcsLogDataHolder.this); + onInitialized.consume(VcsLogDataHolder.this, dataPack); loadAllLog(); // after first part is loaded and shown to the user, start loading the whole log in background } }); + initSw.report(); } }, "Loading recent history..."); } private void readCurrentUser() { + StopWatch sw = StopWatch.start("readCurrentUser"); for (Map.Entry entry : myLogProviders.entrySet()) { VirtualFile root = entry.getKey(); try { @@ -249,6 +250,7 @@ public class VcsLogDataHolder implements Disposable { LOG.warn("Couldn't read the username from root " + root, e); } } + sw.report(); } private void resetState() { @@ -271,6 +273,7 @@ public class VcsLogDataHolder implements Disposable { runInBackground(new ThrowableConsumer() { @Override public void consume(ProgressIndicator indicator) throws VcsException { + StopWatch methodLog = StopWatch.start("loadAllLog"); try { Consumer userRegistry = new Consumer() { @Override @@ -283,13 +286,17 @@ public class VcsLogDataHolder implements Disposable { for (Map.Entry entry : myLogProviders.entrySet()) { VirtualFile root = entry.getKey(); VcsLogProvider logProvider = entry.getValue(); - logs.put(root, compactHashes(logProvider.readAllHashes(root, userRegistry))); + StopWatch sw = StopWatch.start("readAllHashes for " + root.getName()); + List allCommits = logProvider.readAllHashes(root, userRegistry); + sw.report(); + logs.put(root, compactHashes(allCommits)); refs.put(root, logProvider.readAllRefs(root)); } DataPack existingDataPack = myLogData.getDataPack(); // keep existing data pack: we don't want to rebuild the graph, // we just make the whole log structure available for our cunning refresh procedure of if user requests the whole graph myLogData = new LogData(logs, refs, myLogData.getTopCommits(), existingDataPack, true); + methodLog.report(); } finally { myEntireLogLoadWaiter.countDown(); @@ -342,13 +349,14 @@ public class VcsLogDataHolder implements Disposable { // } List compoundLog = myMultiRepoJoiner.join(myLogData.myLogsByRoot.values()); - DataPack fullDataPack = DataPack.build(convertToGraphCommits(compoundLog), myLogData.getAllRefs(), indicator, myHashGetter, myIndexGetter); + final DataPack fullDataPack = DataPack.build(convertToGraphCommits(compoundLog), myLogData.getAllRefs(), indicator, + myIndexGetter, myHashGetter, myLogProviders); myLogData = new LogData(myLogData.getLogs(), myLogData.getRefs(), myLogData.getTopCommits(), fullDataPack, true); myFullLogShowing = true; invokeAndWait(new Runnable() { @Override public void run() { - notifyAboutDataRefresh(); + notifyAboutDataRefresh(fullDataPack); onSuccess.run(); } }); @@ -372,6 +380,7 @@ public class VcsLogDataHolder implements Disposable { if (myLogData == null || !myLogData.isFullLogReady()) { LOG.error("The full log is not ready!"); } + StopWatch methodLog = StopWatch.start("smartRefresh"); Map> logsToBuild = ContainerUtil.newHashMap(); Map> refsByRoot = ContainerUtil.newHashMap(); @@ -402,11 +411,12 @@ public class VcsLogDataHolder implements Disposable { List logToBuild = myFullLogShowing ? compoundLog : topPartOfTheLog; // keep looking at the full log after refresh DataPack dataPack = DataPack.build(convertToGraphCommits(logToBuild), collectAllRefs(refsByRoot), indicator, - myHashGetter, myIndexGetter); + myIndexGetter, myHashGetter, myLogProviders); myLogData = new LogData(logsToBuild, refsByRoot, topPartOfTheLog, dataPack, true); handleOnSuccessInEdt(onSuccess, dataPack); + methodLog.report(); } /** @@ -420,6 +430,7 @@ public class VcsLogDataHolder implements Disposable { * doesn't change the saved log skeleton. */ private void loadFromVcs(int commitCount, ProgressIndicator indicator, final Consumer onSuccess) throws VcsException { + StopWatch methodSW = StopWatch.start("loadFromVcs"); Map> logsToBuild = ContainerUtil.newHashMap(); Map> refsByRoot = ContainerUtil.newHashMap(); @@ -428,17 +439,22 @@ public class VcsLogDataHolder implements Disposable { RecentCommitsInfo info = entry.getValue(); // in this case new commits won't be attached to the log, but will substitute existing ones. - logsToBuild.put(root, info.firstBlockCommits); + List firstBlockCommits = new VcsLogSorter().sortByDateTopoOrder(info.firstBlockCommits); + logsToBuild.put(root, firstBlockCommits); refsByRoot.put(root, info.newRefs); } + StopWatch sw = StopWatch.start("multi-repo join"); List compoundLog = myMultiRepoJoiner.join(logsToBuild.values()); + sw.report(); // even if the full log was already loaded (and possibly presented to the user), // build only the data that was retrieved from the VCS: // if it is not one of the initial refreshes, then it is filtering, and then the DataPack will change anyway. + sw = StopWatch.start("DataPack.build"); DataPack dataPack = DataPack.build(convertToGraphCommits(compoundLog), collectAllRefs(refsByRoot), indicator, - myHashGetter, myIndexGetter); + myIndexGetter, myHashGetter, myLogProviders); + sw.report(); if (myLogData != null && myLogData.isFullLogReady()) { // reuse the skeleton, since it didn't change, because it is not a refresh @@ -451,22 +467,29 @@ public class VcsLogDataHolder implements Disposable { myContainingBranchesGetter.clearCache(); handleOnSuccessInEdt(onSuccess, dataPack); + methodSW.report(); } private Set> collectInfoFromVcs(boolean ordered, int commitsCount) throws VcsException { + StopWatch methodTime = StopWatch.start("collectInfoFromVcs"); Map infoByRoot = ContainerUtil.newHashMap(); for (Map.Entry entry : myLogProviders.entrySet()) { VirtualFile root = entry.getKey(); VcsLogProvider logProvider = entry.getValue(); + StopWatch sw = StopWatch.start("readFirstBlock for " + root.getName()); List firstBlockDetails = logProvider.readFirstBlock(root, ordered, commitsCount); + sw.report(); + sw = StopWatch.start("readAllRefs for" + root.getName()); Collection newRefs = logProvider.readAllRefs(root); + sw.report(); storeTopCommitsDetailsInCache(firstBlockDetails); storeUsers(firstBlockDetails); List firstBlockCommits = getCommitsFromDetails(firstBlockDetails); infoByRoot.put(root, new RecentCommitsInfo(firstBlockCommits, newRefs)); } + methodTime.report(); return infoByRoot.entrySet(); } @@ -483,51 +506,41 @@ public class VcsLogDataHolder implements Disposable { } public void getFilteredDetailsFromTheVcs(@NotNull final VcsLogFilterCollection filterCollection, - @NotNull final Consumer> success, + @NotNull final Consumer>> success, final int maxCount) { runInBackground(new ThrowableConsumer() { @Override public void consume(ProgressIndicator indicator) throws VcsException { Collection> logs = ContainerUtil.newArrayList(); - final Map allDetails = ContainerUtil.newHashMap(); for (Map.Entry entry : myLogProviders.entrySet()) { - VirtualFile root = entry.getKey(); + final VirtualFile root = entry.getKey(); if (filterCollection.getStructureFilter() != null && filterCollection.getStructureFilter().getFiles(root).isEmpty()) { // there is a structure filter, but it doesn't match this root continue; } - List details = entry.getValue().getFilteredDetails(root, filterCollection, maxCount); - logs.add(getCommitsFromDetails(details)); - for (VcsFullCommitDetails detail : details) { - allDetails.put(detail.getHash(), detail); - } + List details = ContainerUtil.map(entry.getValue().getCommitsMatchingFilter(root, filterCollection, maxCount), + new Function() { + @Override + public CommitWithRoot fun(TimedVcsCommit timedVcsCommit) { + return new CommitWithRoot(root, timedVcsCommit.getHash(), + timedVcsCommit.getParents(), + timedVcsCommit.getTime()); + } + }); + logs.add(details); } final List compoundLog = myMultiRepoJoiner.join(logs); - final List list = ContainerUtil.mapNotNull(compoundLog, new Function() { + final List> list = ContainerUtil.map(compoundLog, new Function>() { @Override - public VcsFullCommitDetails fun(TimedVcsCommit commit) { - VcsFullCommitDetails detail = allDetails.get(commit.getHash()); - if (detail == null) { - String message = "Details not stored for commit " + commit; - if (LOG.isDebugEnabled()) { - LOG.error(message, new Attachment("filtered_details", allDetails.toString()), - new Attachment("compound_log", compoundLog.toString())); - } - else { - LOG.error(message); - } - } - return detail; + public Pair fun(TimedVcsCommit timedVcsCommit) { + return Pair.create(timedVcsCommit.getHash(), ((CommitWithRoot)timedVcsCommit).myRoot); } }); - myDetailsGetter.saveInCache(list); - myMiniDetailsGetter.saveInCache(list); - invokeAndWait(new Runnable() { @Override public void run() { @@ -538,6 +551,16 @@ public class VcsLogDataHolder implements Disposable { }, "Looking for more results..."); } + private static class CommitWithRoot extends TimedVcsCommitImpl { + + @NotNull private final VirtualFile myRoot; + + public CommitWithRoot(@NotNull VirtualFile root, @NotNull Hash hash, @NotNull List parents, long timeStamp) { + super(hash, parents, timeStamp); + myRoot = root; + } + } + @NotNull private static List filterStructureFiltersByRoot(@NotNull final VirtualFile root, @NotNull List structureFilters) { @@ -586,6 +609,23 @@ public class VcsLogDataHolder implements Disposable { return myContainingBranchesGetter; } + @Nullable + public Hash findHashByString(@NotNull String string) { + final String pHash = string.toLowerCase(); + try { + return myHashMap.findHash(new Condition() { + @Override + public boolean value(@NotNull Hash hash) { + return hash.toString().toLowerCase().startsWith(pHash); + } + }); + } + catch (IOException e) { + LOG.error(e); + return null; + } + } + private static class RecentCommitsInfo { List firstBlockCommits; Collection newRefs; @@ -646,14 +686,14 @@ public class VcsLogDataHolder implements Disposable { }); } - private void refresh(@NotNull final Runnable onSuccess) { + private void refresh(@NotNull final Consumer onSuccess) { runInBackground(new ThrowableConsumer() { @Override public void consume(ProgressIndicator indicator) throws VcsException { Consumer success = new Consumer() { @Override public void consume(DataPack dataPack) { - onSuccess.run(); + onSuccess.consume(dataPack); } }; @@ -672,10 +712,10 @@ public class VcsLogDataHolder implements Disposable { * It fairly retrieves the data from the VCS and rebuilds the whole log. */ public void refreshCompletely() { - initialize(new Consumer() { + initialize(new PairConsumer() { @Override - public void consume(VcsLogDataHolder holder) { - notifyAboutDataRefresh(); + public void consume(VcsLogDataHolder holder, DataPack dataPack) { + notifyAboutDataRefresh(dataPack); } }); } @@ -686,10 +726,10 @@ public class VcsLogDataHolder implements Disposable { */ public void refresh(@NotNull VirtualFile root) { // TODO refresh only the given root, not all roots - refresh(new Runnable() { + refresh(new Consumer() { @Override - public void run() { - notifyAboutDataRefresh(); + public void consume(@NotNull DataPack dataPack) { + notifyAboutDataRefresh(dataPack); } }); } @@ -702,19 +742,14 @@ public class VcsLogDataHolder implements Disposable { refresh(root); } - @NotNull - public DataPack getDataPack() { - return myLogData.getDataPack(); - } - @Nullable public VcsFullCommitDetails getTopCommitDetails(@NotNull Hash hash) { return myTopCommitsDetailsCache.get(hash); } - private void notifyAboutDataRefresh() { + private void notifyAboutDataRefresh(DataPack dataPack) { if (!myProject.isDisposed()) { - myProject.getMessageBus().syncPublisher(REFRESH_COMPLETED).run(); + myProject.getMessageBus().syncPublisher(REFRESH_COMPLETED).refresh(dataPack); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFilterer.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFilterer.java index e3b22ce32e0b..811c235f3cbe 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFilterer.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFilterer.java @@ -3,23 +3,25 @@ package com.intellij.vcs.log.data; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.EmptyRunnable; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.*; -import com.intellij.vcs.log.graph.elements.Node; -import com.intellij.vcs.log.graphmodel.GraphModel; +import com.intellij.vcs.log.impl.VcsLogUtil; import com.intellij.vcs.log.ui.VcsLogUI; import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel; +import com.intellij.vcs.log.ui.tables.EmptyTableModel; import com.intellij.vcs.log.ui.tables.GraphTableModel; import com.intellij.vcs.log.ui.tables.NoGraphTableModel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Set; public class VcsLogFilterer { @@ -27,25 +29,19 @@ public class VcsLogFilterer { private static final int LOAD_MORE_COMMITS_FIRST_STEP_LIMIT = 200; - private static final Function ALL_NODES_VISIBLE = new Function() { - @Override - public Boolean fun(Node node) { - return true; - } - }; - @NotNull private final VcsLogDataHolder myLogDataHolder; @NotNull private final VcsLogUI myUI; + // TODO remove after new Graph supports filtering + private static final boolean USE_NEW_GRAPH_FOR_FILTERING = true; + public VcsLogFilterer(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUI ui) { myLogDataHolder = logDataHolder; myUI = ui; } - public void applyFiltersAndUpdateUi(@NotNull VcsLogFilterCollection filters) { - DataPack dataPack = myLogDataHolder.getDataPack(); - final GraphModel graphModel = dataPack.getGraphModel(); - List graphFilters = filters.getGraphFilters(); + @NotNull + public AbstractVcsLogTableModel applyFiltersAndUpdateUi(@NotNull DataPack dataPack, @NotNull VcsLogFilterCollection filters) { List detailsFilters = filters.getDetailsFilters(); // it is important to apply graph filters first: @@ -53,54 +49,80 @@ public class VcsLogFilterer { // (e.g. won't be able to find out if a commit belongs to the branch selected by user). // hide invisible nodes from the graph - if (!graphFilters.isEmpty()) { - applyGraphFilters(graphModel, graphFilters); - } - else { - myUI.getTable().executeWithoutRepaint(new Runnable() { - @Override - public void run() { - graphModel.setVisibleBranchesNodes(ALL_NODES_VISIBLE); - } - }); - } + applyGraphFilters(dataPack, filters.getBranchFilter()); // apply details filters, and use simple table without graph (we can't filter by details and keep the graph yet). final AbstractVcsLogTableModel model; - if (!detailsFilters.isEmpty()) { - List filteredCommits = filterByDetails(graphModel, detailsFilters); - model = new NoGraphTableModel(myUI, filteredCommits, dataPack.getRefsModel(), LoadMoreStage.INITIAL); + if (USE_NEW_GRAPH_FOR_FILTERING) { + model = updateFacadeAndCreateModel(dataPack, detailsFilters); } else { - model = new GraphTableModel(myLogDataHolder, myUI); + if (!detailsFilters.isEmpty()) { + List> filteredCommits = filterByDetails(dataPack, detailsFilters); + model = new NoGraphTableModel(dataPack, myLogDataHolder, myUI, filteredCommits, LoadMoreStage.INITIAL); + } + else { + model = new GraphTableModel(dataPack, myLogDataHolder, myUI, LoadMoreStage.INITIAL); + } } - - updateUi(model); + return model; } - private void updateUi(final AbstractVcsLogTableModel model) { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - myUI.setModel(model); - myUI.updateUI(); + private AbstractVcsLogTableModel updateFacadeAndCreateModel(DataPack dataPack, List detailsFilters) { + if (!detailsFilters.isEmpty()) { + List> filteredCommits = filterByDetails(dataPack, detailsFilters); + if (filteredCommits.isEmpty()) { + return new EmptyTableModel(dataPack, myLogDataHolder, myUI, LoadMoreStage.INITIAL); + } + else{ + Condition filter = getFilterFromCommits(filteredCommits); + dataPack.getGraphFacade().setFilter(filter); + } + } + else { + dataPack.getGraphFacade().setFilter(null); + } + return new GraphTableModel(dataPack, myLogDataHolder, myUI, LoadMoreStage.INITIAL); + } - if (model.getRowCount() == 0) { - model.requestToLoadMore(EmptyRunnable.INSTANCE); - } + private Condition getFilterFromCommits(List> filteredCommits) { + final Set commitSet = ContainerUtil.map2Set(filteredCommits, new Function, Integer>() { + @Override + public Integer fun(Pair pair) { + return myLogDataHolder.putHash(pair.getFirst()); } }); + return new Condition() { + @Override + public boolean value(Integer integer) { + return commitSet.contains(integer); + } + }; } - public void requestVcs(@NotNull VcsLogFilterCollection filters, final LoadMoreStage loadMoreStage, @NotNull final Runnable onSuccess) { + public void requestVcs(@NotNull final DataPack dataPack, @NotNull VcsLogFilterCollection filters, + @NotNull final LoadMoreStage loadMoreStage, @NotNull final Runnable onSuccess) { ApplicationManager.getApplication().assertIsDispatchThread(); int maxCount = loadMoreStage == LoadMoreStage.INITIAL ? LOAD_MORE_COMMITS_FIRST_STEP_LIMIT : -1; - myLogDataHolder.getFilteredDetailsFromTheVcs(filters, new Consumer>() { + myLogDataHolder.getFilteredDetailsFromTheVcs(filters, new Consumer>>() { @Override - public void consume(List details) { + public void consume(List> details) { LoadMoreStage newLoadMoreStage = advanceLoadMoreStage(loadMoreStage); - myUI.setModel(new NoGraphTableModel(myUI, details, myLogDataHolder.getDataPack().getRefsModel(), newLoadMoreStage)); - myUI.updateUI(); + AbstractVcsLogTableModel model; + if (!USE_NEW_GRAPH_FOR_FILTERING) { + model = new NoGraphTableModel(dataPack, myLogDataHolder, myUI, details, newLoadMoreStage); + } + else { + if (details.isEmpty()) { + model = new EmptyTableModel(dataPack, myLogDataHolder, myUI, newLoadMoreStage); + } + else { + dataPack.getGraphFacade().setFilter(getFilterFromCommits(details)); + model = new GraphTableModel(dataPack, myLogDataHolder, myUI, newLoadMoreStage); + } + } + myUI.setModel(model); + myUI.repaintUI(); onSuccess.run(); } }, maxCount); @@ -122,36 +144,23 @@ public class VcsLogFilterer { return newLoadMoreStage; } - private void applyGraphFilters(final GraphModel graphModel, final List onGraphFilters) { + private void applyGraphFilters(@NotNull final DataPack dataPack, @Nullable final VcsLogBranchFilter branchFilter) { myUI.getTable().executeWithoutRepaint(new Runnable() { @Override public void run() { - graphModel.setVisibleBranchesNodes(new Function() { - @Override - public Boolean fun(final Node node) { - return !ContainerUtil.exists(onGraphFilters, new Condition() { - @Override - public boolean value(VcsLogGraphFilter filter) { - return !filter.matches(node.getCommitIndex()); - } - }); - } - }); + dataPack.getGraphFacade().setVisibleBranches(branchFilter != null ? branchFilter.getMatchingHeads() : null); } }); } @NotNull - private List filterByDetails(@NotNull GraphModel graphModel, @NotNull List detailsFilters) { - List result = ContainerUtil.newArrayList(); + private List> filterByDetails(@NotNull DataPack dataPack, @NotNull List detailsFilters) { + List> result = ContainerUtil.newArrayList(); int topCommits = myLogDataHolder.getSettings().getRecentCommitsCount(); - for (int i = 0; i < topCommits && i < graphModel.getGraph().getNodeRows().size(); i++) { - Node node = graphModel.getGraph().getCommitNodeInRow(i); - if (node == null) { - // there can be nodes which contain no commits (IDEA-115442, branch filter case) - continue; - } - final VcsFullCommitDetails details = getDetailsFromCache(node); + List visibleCommits = VcsLogUtil.getVisibleCommits(dataPack.getGraphFacade()); + for (int i = 0; i < topCommits && i < visibleCommits.size(); i++) { + int commitIndex = visibleCommits.get(i); + final VcsFullCommitDetails details = getDetailsFromCache(commitIndex); if (details == null) { // Details for recent commits should be available in the cache. // However if they are not there for some reason, we stop filtering. @@ -160,7 +169,7 @@ public class VcsLogFilterer { // then we will return the list which incorrectly misses some matching commit in the middle. // => Instead we rather will return a smaller list: this is not a problem, // because the VCS will be requested for filtered details if there are not enough of them. - LOG.debug("No details found for a recent commit " + myLogDataHolder.getHash(node.getCommitIndex())); + LOG.debug("No details found for a recent commit " + myLogDataHolder.getHash(commitIndex)); break; } boolean allFiltersMatch = !ContainerUtil.exists(detailsFilters, new Condition() { @@ -170,19 +179,19 @@ public class VcsLogFilterer { } }); if (allFiltersMatch) { - result.add(details); + result.add(Pair.create(details.getHash(), details.getRoot())); } } return result; } @Nullable - private VcsFullCommitDetails getDetailsFromCache(@NotNull final Node node) { + private VcsFullCommitDetails getDetailsFromCache(final int commitIndex) { final Ref ref = Ref.create(); UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { - ref.set(myLogDataHolder.getCommitDetailsGetter().getCommitDataIfAvailable(myLogDataHolder.getHash(node.getCommitIndex()))); + ref.set(myLogDataHolder.getCommitDetailsGetter().getCommitDataIfAvailable(myLogDataHolder.getHash(commitIndex))); } }); return ref.get(); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogHashMap.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogHashMap.java index a3a520376909..e62d22c8b22f 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogHashMap.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogHashMap.java @@ -19,7 +19,10 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.ThrowableComputable; +import com.intellij.util.CommonProcessors; import com.intellij.util.io.IOUtil; import com.intellij.util.io.KeyDescriptor; import com.intellij.util.io.Page; @@ -77,6 +80,22 @@ class VcsLogHashMap implements Disposable { } } + @Nullable + Hash findHash(@NotNull final Condition condition) throws IOException { + final Ref hashRef = Ref.create(); + myPersistentEnumerator.iterateData(new CommonProcessors.FindProcessor() { + @Override + protected boolean accept(Hash hash) { + boolean matches = condition.value(hash); + if (matches) { + hashRef.set(hash); + } + return matches; + } + }); + return hashRef.get(); + } + private static class MyHashKeyDescriptor implements KeyDescriptor { @Override public void save(@NotNull DataOutput out, Hash value) throws IOException { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogJoiner.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogJoiner.java index 1450371109d6..1a4c81ad3662 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogJoiner.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogJoiner.java @@ -71,7 +71,7 @@ public class VcsLogJoiner { unsafePartSavedLog.add(commit); } } - unsafePartSavedLog = new NewCommitIntegrator(unsafePartSavedLog, allNewsCommits).getResultList(); + unsafePartSavedLog = new NewCommitIntegrator(unsafePartSavedLog, allNewsCommits).getResultList(); return Pair.create(ContainerUtil.concat(unsafePartSavedLog, savedLog.subList(unsafeBlockSize, savedLog.size())), unsafePartSavedLog.size() - unsafeBlockSize); @@ -205,52 +205,56 @@ public class VcsLogJoiner { } - private static class NewCommitIntegrator { - private final List list; - private final Map newCommitsMap; - private final List newSortedCommits; + /*package*/ static class NewCommitIntegrator { + private final List list; + private final Map newCommitsMap; + private final Collection newCommits; - private NewCommitIntegrator(@NotNull List list, @NotNull Set newCommits) { + public NewCommitIntegrator(@NotNull List list, @NotNull Collection newCommits) { this.list = list; newCommitsMap = ContainerUtil.newHashMap(); - for (TimedVcsCommit commit : newCommits) { + this.newCommits = newCommits; + for (Commit commit : newCommits) { newCommitsMap.put(commit.getHash(), commit); } - newSortedCommits = new ArrayList(newCommits); - Collections.sort(newSortedCommits, new Comparator() { - @Override - public int compare(@NotNull TimedVcsCommit o1, @NotNull TimedVcsCommit o2) { - return new Long(o1.getTime()).compareTo(o2.getTime()); - } - }); } // return insert Index - private void insertToList(@NotNull TimedVcsCommit commit) { + private void insertToList(@NotNull Commit commit) { if (!newCommitsMap.containsKey(commit.getHash())) { throw new IllegalStateException("Commit was inserted, but insert call again. Commit hash: " + commit.getHash()); } //insert all parents commits for (Hash parentHash : commit.getParents()) { - TimedVcsCommit parentCommit = newCommitsMap.get(parentHash); + Commit parentCommit = newCommitsMap.get(parentHash); if (parentCommit != null) { insertToList(parentCommit); } } - list.add(0, commit); + int insertIndex; + HashSet parents = new HashSet(commit.getParents()); + for (insertIndex = 0; insertIndex < list.size(); insertIndex++) { + Commit currentCommit = list.get(insertIndex); + if (parents.contains(currentCommit.getHash())) + break; + if (currentCommit.getTime() < commit.getTime()) + break; + } + + list.add(insertIndex, commit); newCommitsMap.remove(commit.getHash()); } private void insertAllCommits() { - for (TimedVcsCommit commit : newSortedCommits) { + for (Commit commit : newCommits) { if (newCommitsMap.get(commit.getHash()) != null) { insertToList(commit); } } } - private List getResultList() { + public List getResultList() { insertAllCommits(); return list; } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefreshListener.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefreshListener.java new file mode 100644 index 000000000000..4a9799264f4d --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefreshListener.java @@ -0,0 +1,27 @@ +/* + * 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.vcs.log.data; + +import org.jetbrains.annotations.NotNull; + +/** + * This listener is notified about VCS log refresh procedure. + */ +public interface VcsLogRefreshListener { + + void refresh(@NotNull DataPack dataPack); + +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogSorter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogSorter.java new file mode 100644 index 000000000000..344b3ed50f89 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogSorter.java @@ -0,0 +1,31 @@ +/* + * 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.vcs.log.data; + +import com.intellij.vcs.log.TimedVcsCommit; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +class VcsLogSorter { + + @NotNull + List sortByDateTopoOrder(@NotNull List commits) { + return new VcsLogJoiner.NewCommitIntegrator(new ArrayList(), commits).getResultList(); + } + +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java index b3638aaa143d..1955bfbba67e 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java @@ -42,6 +42,7 @@ public class VcsLogUiProperties implements PersistentStateComponent RECENTLY_FILTERED_USER_GROUPS = new ArrayDeque(); + public Deque RECENTLY_FILTERED_BRANCH_GROUPS = new ArrayDeque(); } @Nullable @@ -68,20 +69,38 @@ public class VcsLogUiProperties implements PersistentStateComponent usersInGroup) { + addRecentGroup(usersInGroup, myState.RECENTLY_FILTERED_USER_GROUPS); + } + + public void addRecentlyFilteredBranchGroup(@NotNull List usersInGroup) { + addRecentGroup(usersInGroup, myState.RECENTLY_FILTERED_BRANCH_GROUPS); + } + + private static void addRecentGroup(@NotNull List usersInGroup, @NotNull Deque stateField) { UserGroup group = new UserGroup(); group.users = usersInGroup; - if (myState.RECENTLY_FILTERED_USER_GROUPS.contains(group)) { + if (stateField.contains(group)) { return; } - myState.RECENTLY_FILTERED_USER_GROUPS.addFirst(group); - if (myState.RECENTLY_FILTERED_USER_GROUPS.size() > RECENTLY_FILTERED_USERS_AMOUNT) { - myState.RECENTLY_FILTERED_USER_GROUPS.removeLast(); + stateField.addFirst(group); + if (stateField.size() > RECENTLY_FILTERED_USERS_AMOUNT) { + stateField.removeLast(); } } @NotNull public List> getRecentlyFilteredUserGroups() { - return ContainerUtil.map2List(myState.RECENTLY_FILTERED_USER_GROUPS, new Function>() { + return getRecentGroup(myState.RECENTLY_FILTERED_USER_GROUPS); + } + + @NotNull + public List> getRecentlyFilteredBranchGroups() { + return getRecentGroup(myState.RECENTLY_FILTERED_BRANCH_GROUPS); + } + + @NotNull + private static List> getRecentGroup(Deque stateField) { + return ContainerUtil.map2List(stateField, new Function>() { @Override public List fun(UserGroup group) { return group.users; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistry.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistry.java index bd29145b626c..3e9181ff4b55 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistry.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistry.java @@ -15,11 +15,15 @@ */ package com.intellij.vcs.log.data; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Interner; import com.intellij.vcs.log.VcsUser; import com.intellij.vcs.log.impl.VcsUserImpl; +import org.jetbrains.annotations.NotNull; import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @@ -27,16 +31,33 @@ import java.util.Set; public class VcsUserRegistry { private final Interner myUserMap = new Interner(); + private final ReadWriteLock myLock = new ReentrantReadWriteLock(); - public VcsUser createUser(String name, String email) { - return myUserMap.intern(new VcsUserImpl(name, email)); + @NotNull + public VcsUser createUser(@NotNull String name, @NotNull String email) { + return addUser(new VcsUserImpl(name, email)); } - public void addUser(VcsUser user) { - myUserMap.intern(user); + @NotNull + public VcsUser addUser(@NotNull VcsUser user) { + myLock.writeLock().lock(); + try { + return myUserMap.intern(user); + } + finally { + myLock.writeLock().unlock(); + } + } + @NotNull public Set getUsers() { - return myUserMap.getValues(); + myLock.readLock().lock(); + try { + return ContainerUtil.newHashSet(myUserMap.getValues()); + } + finally { + myLock.readLock().unlock(); + } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/graph/GraphColorManagerImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/graph/GraphColorManagerImpl.java new file mode 100644 index 000000000000..92f4857728fa --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/graph/GraphColorManagerImpl.java @@ -0,0 +1,117 @@ +/* + * 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.vcs.log.graph; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.JBColor; +import com.intellij.util.NotNullFunction; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.Hash; +import com.intellij.vcs.log.VcsLogRefManager; +import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.data.RefsModel; +import com.intellij.vcs.log.graph.render.ColorGenerator; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; +import java.util.Collection; +import java.util.Map; + +public class GraphColorManagerImpl implements GraphColorManager { + + private static final Logger LOG = Logger.getInstance(GraphColorManagerImpl.class); + private static final JBColor DEFAULT_COLOR = JBColor.BLACK; + + @NotNull private final RefsModel myRefsModel; + @NotNull private final NotNullFunction myHashGetter; + @NotNull private final Map myRefManagers; + + public GraphColorManagerImpl(@NotNull RefsModel refsModel, @NotNull NotNullFunction hashGetter, + @NotNull Map refManagers) { + myRefsModel = refsModel; + myHashGetter = hashGetter; + myRefManagers = refManagers; + } + + @NotNull + @Override + public JBColor getColorOfBranch(int headCommit) { + Collection refs = myRefsModel.refsToCommit(headCommit); + if (!checkEmptiness(refs, headCommit)) { + return DEFAULT_COLOR; + } + VcsRef firstRef = getRefManager(refs).sort(refs).get(0); + Color color = ColorGenerator.getColor(firstRef.getName().hashCode()); + // TODO dark variant + return new JBColor(color, color); + } + + private boolean checkEmptiness(@NotNull Collection refs, int head) { + if (refs.isEmpty()) { + LOG.error("No references found at head " + head + " which corresponds to hash " + myHashGetter.fun(head)); + return false; + } + return true; + } + + @NotNull + @Override + public JBColor getColorOfFragment(int headCommit, int magicIndex) { + Color color = ColorGenerator.getColor(magicIndex); + return new JBColor(color, color); + } + + @Override + public int compareHeads(int head1, int head2) { + if (head1 == head2) { + return 0; + } + + Collection refs1 = myRefsModel.refsToCommit(head1); + Collection refs2 = myRefsModel.refsToCommit(head2); + if (!checkEmptiness(refs1, head1)) { + return -1; + } + if (!checkEmptiness(refs2, head2)) { + return 1; + } + + VcsLogRefManager refManager1 = getRefManager(refs1); + VcsLogRefManager refManager2 = getRefManager(refs2); + if (!refManager1.equals(refManager2)) { + LOG.debug("Different ref managers (and therefore different VCSs) are not comparable"); + return 0; + } + + Map positions = ContainerUtil.newHashMap(); + for (VcsRef ref : refs1) { + positions.put(ref, true); + } + for (VcsRef ref : refs2) { + positions.put(ref, false); + } + + VcsRef firstRef = refManager1.sort(positions.keySet()).get(0); + return positions.get(firstRef) ? 1 : -1; + } + + @NotNull + private VcsLogRefManager getRefManager(@NotNull Collection refs) { + return myRefManagers.get(refs.iterator().next().getRoot()); + } + +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogFilterCollectionImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogFilterCollectionImpl.java index 4920a4ecb59f..285e3ce91079 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogFilterCollectionImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogFilterCollectionImpl.java @@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; -import java.util.Collections; import java.util.List; public class VcsLogFilterCollectionImpl implements VcsLogFilterCollection { @@ -76,13 +75,7 @@ public class VcsLogFilterCollectionImpl implements VcsLogFilterCollection { @Override public boolean isEmpty() { - return getGraphFilters().isEmpty() && getDetailsFilters().isEmpty(); - } - - @NotNull - @Override - public List getGraphFilters() { - return ContainerUtil.skipNulls(Collections.singletonList(myBranchFilter)); + return myBranchFilter == null && getDetailsFilters().isEmpty(); } @NotNull diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java index 96fffeb5c212..244ca14b8ab6 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java @@ -87,7 +87,7 @@ public class VcsLogImpl implements VcsLog { @NotNull @Override public Collection getAllReferences() { - return myDataHolder.getDataPack().getRefsModel().getAllRefs(); + return myUi.getDataPack().getRefsModel().getAllRefs(); } @Override diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java index e06fc4e60ac0..9d19ddd82217 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java @@ -20,12 +20,13 @@ import com.intellij.ui.components.JBLoadingPanel; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManagerAdapter; import com.intellij.ui.content.ContentManagerEvent; -import com.intellij.util.Consumer; +import com.intellij.util.PairConsumer; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.vcs.log.VcsLogProvider; import com.intellij.vcs.log.VcsLogRefresher; import com.intellij.vcs.log.VcsLogSettings; +import com.intellij.vcs.log.data.DataPack; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.data.VcsLogUiProperties; import com.intellij.vcs.log.ui.VcsLogColorManagerImpl; @@ -71,11 +72,11 @@ public class VcsLogManager implements Disposable { final VcsLogContainer mainPanel = new VcsLogContainer(myProject); myLogDataHolder = new VcsLogDataHolder(myProject, this, logProviders, mySettings); - myLogDataHolder.initialize(new Consumer() { + myLogDataHolder.initialize(new PairConsumer() { @Override - public void consume(VcsLogDataHolder vcsLogDataHolder) { + public void consume(VcsLogDataHolder vcsLogDataHolder, DataPack dataPack) { VcsLogUI logUI = new VcsLogUI(vcsLogDataHolder, myProject, mySettings, - new VcsLogColorManagerImpl(logProviders.keySet()), myUiProperties); + new VcsLogColorManagerImpl(logProviders.keySet()), myUiProperties, dataPack); myLogDataHolder = vcsLogDataHolder; myUi = logUI; mainPanel.init(logUI.getMainFrame().getMainComponent()); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogUtil.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogUtil.java index a8f1585e936c..30fba91e8028 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogUtil.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogUtil.java @@ -18,16 +18,11 @@ package com.intellij.vcs.log.impl; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.MultiMap; import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.graph.GraphFacade; import org.jetbrains.annotations.NotNull; -import java.util.Collection; -import java.util.Comparator; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; -/** - * @author Kirill Likhodedov - */ public class VcsLogUtil { @NotNull public static MultiMap groupRefsByRoot(@NotNull Collection refs) { @@ -47,4 +42,20 @@ public class VcsLogUtil { } return map; } + + @NotNull + public static List getVisibleCommits(@NotNull final GraphFacade facade) { + return new AbstractList() { + @Override + public Integer get(int index) { + return facade.getCommitAtRow(index); + } + + @Override + public int size() { + return facade.getVisibleCommitCount(); + } + }; + } + } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java index cda36e4307b2..cd4b72538707 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java @@ -1,9 +1,11 @@ package com.intellij.vcs.log.ui; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.util.PairFunction; import com.intellij.util.containers.ContainerUtil; @@ -12,23 +14,16 @@ import com.intellij.vcs.log.Hash; import com.intellij.vcs.log.VcsLog; import com.intellij.vcs.log.VcsLogFilterCollection; import com.intellij.vcs.log.VcsLogSettings; -import com.intellij.vcs.log.compressedlist.UpdateRequest; -import com.intellij.vcs.log.data.DataPack; -import com.intellij.vcs.log.data.VcsLogDataHolder; -import com.intellij.vcs.log.data.VcsLogFilterer; -import com.intellij.vcs.log.data.VcsLogUiProperties; -import com.intellij.vcs.log.graph.elements.GraphElement; -import com.intellij.vcs.log.graph.elements.Node; -import com.intellij.vcs.log.graphmodel.FragmentManager; -import com.intellij.vcs.log.graphmodel.GraphFragment; +import com.intellij.vcs.log.data.*; +import com.intellij.vcs.log.graph.*; import com.intellij.vcs.log.impl.VcsLogImpl; -import com.intellij.vcs.log.printmodel.SelectController; import com.intellij.vcs.log.ui.frame.MainFrame; import com.intellij.vcs.log.ui.frame.VcsLogGraphTable; import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; import java.awt.*; import java.util.Set; @@ -48,28 +43,27 @@ public class VcsLogUI { @NotNull private final MainFrame myMainFrame; @NotNull private final Project myProject; @NotNull private final VcsLogColorManager myColorManager; - @NotNull private final VcsLogUiProperties myUiProperties; @NotNull private final VcsLogFilterer myFilterer; @NotNull private final VcsLog myLog; - @Nullable private GraphElement prevGraphElement; + @NotNull private DataPack myDataPack; public VcsLogUI(@NotNull VcsLogDataHolder logDataHolder, @NotNull Project project, @NotNull VcsLogSettings settings, - @NotNull VcsLogColorManager manager, @NotNull VcsLogUiProperties uiProperties) { + @NotNull VcsLogColorManager manager, @NotNull VcsLogUiProperties uiProperties, @NotNull DataPack initialDataPack) { myLogDataHolder = logDataHolder; myProject = project; myColorManager = manager; - myUiProperties = uiProperties; + myDataPack = initialDataPack; myFilterer = new VcsLogFilterer(logDataHolder, this); myLog = new VcsLogImpl(myLogDataHolder, this); - myMainFrame = new MainFrame(myLogDataHolder, this, project, settings, uiProperties, myLog); - project.getMessageBus().connect(project).subscribe(VcsLogDataHolder.REFRESH_COMPLETED, new Runnable() { + myMainFrame = new MainFrame(myLogDataHolder, this, project, settings, uiProperties, myLog, initialDataPack); + project.getMessageBus().connect(project).subscribe(VcsLogDataHolder.REFRESH_COMPLETED, new VcsLogRefreshListener() { @Override - public void run() { - applyFiltersAndUpdateUi(); + public void refresh(@NotNull DataPack dataPack) { + applyFiltersAndUpdateUi(dataPack); } }); - applyFiltersAndUpdateUi(); + applyFiltersAndUpdateUi(initialDataPack); } @NotNull @@ -82,7 +76,6 @@ public class VcsLogUI { @Override public void run() { myMainFrame.getGraphTable().jumpToRow(rowIndex); - click(rowIndex); } }); } @@ -133,31 +126,15 @@ public class VcsLogUI { return rowsToSelect; } - public void updateUI() { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - myMainFrame.getGraphTable().repaint(); - } - }); - } - - public void addToSelection(final Hash hash) { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - int row = myLogDataHolder.getDataPack().getRowByHash(hash); - myMainFrame.getGraphTable().getSelectionModel().addSelectionInterval(row, row); - } - }); + public void repaintUI() { + myMainFrame.getGraphTable().repaint(); } public void showAll() { runUnderModalProgress("Expanding linear branches...", new Runnable() { @Override public void run() { - myLogDataHolder.getDataPack().getGraphModel().getFragmentManager().showAll(); - updateUI(); + handleAnswer(myDataPack.getGraphFacade().performAction(LinearBranchesExpansionAction.EXPAND)); jumpToRow(0); } }); @@ -167,73 +144,22 @@ public class VcsLogUI { runUnderModalProgress("Collapsing linear branches...", new Runnable() { @Override public void run() { - myLogDataHolder.getDataPack().getGraphModel().getFragmentManager().hideAll(); - updateUI(); + handleAnswer(myDataPack.getGraphFacade().performAction(LinearBranchesExpansionAction.COLLAPSE)); jumpToRow(0); } }); } public void setLongEdgeVisibility(boolean visibility) { - myLogDataHolder.getDataPack().getPrintCellModel().setLongEdgeVisibility(visibility); - updateUI(); + handleAnswer(myDataPack.getGraphFacade().performAction(LongEdgesAction.valueOf(visibility))); } public boolean areLongEdgesHidden() { - return myLogDataHolder.getDataPack().getPrintCellModel().areLongEdgesHidden(); - } - - public void over(@Nullable GraphElement graphElement) { - SelectController selectController = myLogDataHolder.getDataPack().getPrintCellModel().getSelectController(); - FragmentManager fragmentManager = myLogDataHolder.getDataPack().getGraphModel().getFragmentManager(); - if (graphElement == prevGraphElement) { - return; - } - else { - prevGraphElement = graphElement; - } - selectController.deselectAll(); - if (graphElement == null) { - updateUI(); - } - else { - GraphFragment graphFragment = fragmentManager.relateFragment(graphElement); - selectController.select(graphFragment); - updateUI(); - } - } - - public void click(@Nullable GraphElement graphElement) { - SelectController selectController = myLogDataHolder.getDataPack().getPrintCellModel().getSelectController(); - final FragmentManager fragmentController = myLogDataHolder.getDataPack().getGraphModel().getFragmentManager(); - selectController.deselectAll(); - if (graphElement == null) { - return; - } - final GraphFragment fragment = fragmentController.relateFragment(graphElement); - if (fragment == null) { - return; - } - - myMainFrame.getGraphTable().executeWithoutRepaint(new Runnable() { - @Override - public void run() { - UpdateRequest updateRequest = fragmentController.changeVisibility(fragment); - jumpToRow(updateRequest.from()); - } - }); - updateUI(); + return myDataPack.getGraphFacade().getInfoProvider().areLongEdgesHidden(); } public void click(int rowIndex) { - DataPack dataPack = myLogDataHolder.getDataPack(); - dataPack.getPrintCellModel().getCommitSelectController().deselectAll(); - Node node = dataPack.getNode(rowIndex); - if (node != null) { - FragmentManager fragmentController = dataPack.getGraphModel().getFragmentManager(); - dataPack.getPrintCellModel().getCommitSelectController().select(fragmentController.allCommitsCurrentBranch(node)); - } - updateUI(); + handleAnswer(myDataPack.getGraphFacade().performAction(new ClickGraphAction(rowIndex, null))); } public void jumpToCommit(@NotNull Hash commitHash) { @@ -254,6 +180,27 @@ public class VcsLogUI { }); } + public void handleAnswer(@Nullable GraphAnswer answer) { + repaintUI(); + + if (answer == null) { + return; + } + GraphChange graphChange = answer.getGraphChange(); + if (graphChange != null) { + ((AbstractTableModel)(getTable().getModel())).fireTableStructureChanged(); + } + + GraphActionRequest actionRequest = answer.getActionRequest(); + if (actionRequest instanceof JumpToRowActionRequest) { + int row = ((JumpToRowActionRequest)actionRequest).getRow(); + jumpToRow(row); + } + else if (actionRequest instanceof ChangeCursorActionRequest) { + myMainFrame.setCursor(((ChangeCursorActionRequest)actionRequest).getCursor()); + } + } + private void jumpTo(@NotNull final T commitId, @NotNull final PairFunction rowGetter) { AbstractVcsLogTableModel model = getModel(); if (model == null) { @@ -311,17 +258,30 @@ public class VcsLogUI { return myFilterer; } - @NotNull - public VcsLogDataHolder getLogDataHolder() { - return myLogDataHolder; + private void applyFiltersAndUpdateUi(@NotNull final DataPack dataPack) { + runUnderModalProgress("Applying filters...", new Runnable() { + public void run() { + final AbstractVcsLogTableModel newModel = myFilterer.applyFiltersAndUpdateUi(dataPack, collectFilters()); + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + myDataPack = dataPack; + setModel(newModel); + myMainFrame.updateDataPack(myDataPack); + repaintUI(); + + if (newModel.getRowCount() == 0) { // getValueAt won't be called for empty model => need to explicitly request to load more + newModel.requestToLoadMore(EmptyRunnable.INSTANCE); + } + } + }); + } + }); } public void applyFiltersAndUpdateUi() { - runUnderModalProgress("Applying filters...", new Runnable() { - public void run() { - myFilterer.applyFiltersAndUpdateUi(collectFilters()); - } - }); + ApplicationManager.getApplication().assertIsDispatchThread(); + applyFiltersAndUpdateUi(myDataPack); } @NotNull @@ -333,18 +293,18 @@ public class VcsLogUI { return myMainFrame.getGraphTable(); } - @NotNull - public VcsLogUiProperties getUiProperties() { - return myUiProperties; - } - @NotNull public Project getProject() { return myProject; } - public void runUnderModalProgress(@NotNull String task, @NotNull Runnable runnable) { - ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, task, false, null, this.getMainFrame().getMainComponent()); + public void runUnderModalProgress(@NotNull final String task, @NotNull final Runnable runnable) { + getTable().executeWithoutRepaint(new Runnable() { + public void run() { + ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, task, false, null, getMainFrame().getMainComponent()); + } + }); + repaintUI(); } public void setBranchesPanelVisible(boolean visible) { @@ -359,4 +319,10 @@ public class VcsLogUI { public VcsLog getVcsLog() { return myLog; } + + @NotNull + public DataPack getDataPack() { + ApplicationManager.getApplication().assertIsDispatchThread(); + return myDataPack; + } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/BranchFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/BranchFilterPopupComponent.java index 2a42b6e7f79a..7d650c5af9e0 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/BranchFilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/BranchFilterPopupComponent.java @@ -18,23 +18,38 @@ package com.intellij.vcs.log.ui.filter; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; +import com.intellij.vcs.log.data.RefsModel; import com.intellij.vcs.log.data.VcsLogBranchFilterImpl; +import com.intellij.vcs.log.data.VcsLogDataHolder; +import com.intellij.vcs.log.data.VcsLogUiProperties; import com.intellij.vcs.log.impl.VcsLogUtil; -import com.intellij.vcs.log.ui.VcsLogUI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -class BranchFilterPopupComponent extends FilterPopupComponent { +class BranchFilterPopupComponent extends MultipleValueFilterPopupComponent { - @NotNull private final VcsLogUI myUi; + @NotNull private final VcsLogDataHolder myDataHolder; + @NotNull private final VcsLogUiProperties myUiProperties; - BranchFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull VcsLogUI ui) { + @NotNull private RefsModel myRefsModel; + + BranchFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, + @NotNull VcsLogDataHolder dataHolder, + @NotNull RefsModel refsModel, + @NotNull VcsLogUiProperties uiProperties) { super(filterUi, "Branch"); - myUi = ui; + myDataHolder = dataHolder; + myRefsModel = refsModel; + myUiProperties = uiProperties; + } + + void updateRefsModel(@NotNull RefsModel refsModel) { + myRefsModel = refsModel; } @Override @@ -42,13 +57,14 @@ class BranchFilterPopupComponent extends FilterPopupComponent allRefs = myUi.getLogDataHolder().getDataPack().getRefsModel().getBranches(); + Collection allRefs = myRefsModel.getBranches(); for (Map.Entry> entry : VcsLogUtil.groupRefsByRoot(allRefs).entrySet()) { VirtualFile root = entry.getKey(); Collection refs = entry.getValue(); - VcsLogProvider provider = myUi.getLogDataHolder().getLogProvider(root); + VcsLogProvider provider = myDataHolder.getLogProvider(root); VcsLogRefManager refManager = provider.getReferenceManager(); List refGroups = refManager.group(refs); @@ -62,19 +78,20 @@ class BranchFilterPopupComponent extends FilterPopupComponent> group : groups.expandedGroups.entrySet()) { actionGroup.addSeparator(group.getKey()); for (String action : group.getValue()) { - actionGroup.add(new SetValueAction(action, this)); + actionGroup.add(createPredefinedValueAction(Collections.singleton(action))); } } actionGroup.addSeparator(); for (Map.Entry> group : groups.collapsedGroups.entrySet()) { DefaultActionGroup popupGroup = new DefaultActionGroup(group.getKey(), true); for (String action : group.getValue()) { - popupGroup.add(new SetValueAction(action, this)); + popupGroup.add(createPredefinedValueAction(Collections.singleton(action))); } actionGroup.add(popupGroup); } @@ -125,9 +142,35 @@ class BranchFilterPopupComponent extends FilterPopupComponent allBranches = myUi.getLogDataHolder().getDataPack().getRefsModel().getBranches(); - return value == ALL ? null : new VcsLogBranchFilterImpl(allBranches, Collections.singletonList(value)); + if (getSelectedValues() == null) { + return null; + } + Collection allBranches = myRefsModel.getBranches(); + return new VcsLogBranchFilterImpl(allBranches, getSelectedValues()); + } + + @NotNull + @Override + protected List> getRecentValuesFromSettings() { + return myUiProperties.getRecentlyFilteredBranchGroups(); + } + + @Override + protected void rememberValuesInSettings(@NotNull Collection values) { + if (values.size() > 1) { // all branches are in the popup => no need to save single one, only in case of multiple selection + myUiProperties.addRecentlyFilteredBranchGroup(new ArrayList(values)); + } + } + + @NotNull + @Override + protected List getAllValues() { + return ContainerUtil.map(myRefsModel.getBranches(), new Function() { + @Override + public String fun(VcsRef ref) { + return ref.getName(); + } + }); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterPopupComponent.java index fa7ad5a43bd1..3b98c0b4d38e 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterPopupComponent.java @@ -116,6 +116,7 @@ abstract class FilterPopupComponent extends JPanel /** * Returns the special action that indicates that no filtering is selected in this component. */ + @NotNull protected AnAction createAllAction() { return new AllAction(this); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/MultilinePopupBuilder.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/MultilinePopupBuilder.java new file mode 100644 index 000000000000..bb61a79ffb31 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/MultilinePopupBuilder.java @@ -0,0 +1,137 @@ +/* + * 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.vcs.log.ui.filter; + +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonShortcuts; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.fileTypes.FileTypes; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.ComponentPopupBuilder; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.spellchecker.ui.SpellCheckingEditorCustomization; +import com.intellij.ui.EditorCustomization; +import com.intellij.ui.EditorTextField; +import com.intellij.ui.EditorTextFieldProvider; +import com.intellij.ui.SoftWrapsEditorCustomization; +import com.intellij.util.TextFieldCompletionProviderDumbAware; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.border.CompoundBorder; +import java.awt.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +class MultilinePopupBuilder { + + private static final char[] SEPARATORS = { ',', '|', '\n' }; + + @NotNull private final EditorTextField myTextField; + + MultilinePopupBuilder(@NotNull Project project, @NotNull final Collection values) { + myTextField = createTextField(project); + new MyCompletionProvider(values).apply(myTextField); + } + + @NotNull + private static EditorTextField createTextField(@NotNull Project project) { + final EditorTextFieldProvider service = ServiceManager.getService(project, EditorTextFieldProvider.class); + List + features = Arrays.asList(SoftWrapsEditorCustomization.ENABLED, SpellCheckingEditorCustomization.DISABLED); + EditorTextField textField = service.getEditorField(FileTypes.PLAIN_TEXT.getLanguage(), project, features); + textField.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2), textField.getBorder())); + textField.setOneLineMode(false); + return textField; + } + + @NotNull + JBPopup createPopup() { + JPanel panel = new JPanel(new BorderLayout()); + panel.add(myTextField, BorderLayout.CENTER); + ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, myTextField) + .setCancelOnClickOutside(true) + .setAdText(KeymapUtil.getShortcutsText(CommonShortcuts.CTRL_ENTER.getShortcuts()) + " to finish") + .setMovable(true) + .setRequestFocus(true) + .setResizable(true) + .setMayBeParent(true); + + final JBPopup popup = builder.createPopup(); + popup.setMinimumSize(new Dimension(200, 90)); + AnAction okAction = new DumbAwareAction() { + @Override + public void actionPerformed(AnActionEvent e) { + unregisterCustomShortcutSet(popup.getContent()); + popup.closeOk(e.getInputEvent()); + } + }; + okAction.registerCustomShortcutSet(CommonShortcuts.CTRL_ENTER, popup.getContent()); + return popup; + } + + @NotNull + Collection getSelectedValues() { + return ContainerUtil.toCollection(StringUtil.tokenize(myTextField.getText().trim(), new String(SEPARATORS))); + } + + private static class MyCompletionProvider extends TextFieldCompletionProviderDumbAware { + + @NotNull private final Collection myValues; + + MyCompletionProvider(@NotNull Collection values) { + super(true); + myValues = values; + } + + @NotNull + @Override + protected String getPrefix(@NotNull String currentTextPrefix) { + final int separatorPosition = lastSeparatorPosition(currentTextPrefix); + return separatorPosition == -1 ? currentTextPrefix : currentTextPrefix.substring(separatorPosition + 1).trim(); + } + + private static int lastSeparatorPosition(@NotNull String text) { + int lastPosition = -1; + for (char separator : SEPARATORS) { + int lio = text.lastIndexOf(separator); + if (lio > lastPosition) { + lastPosition = lio; + } + } + return lastPosition; + } + + @Override + protected void addCompletionVariants(@NotNull String text, int offset, @NotNull String prefix, + @NotNull CompletionResultSet result) { + result.addLookupAdvertisement("Select one or more users separated with comma, | or new lines"); + for (String completionVariant : myValues) { + final LookupElementBuilder element = LookupElementBuilder.create(completionVariant); + result.addElement(element.withLookupString(completionVariant.toLowerCase())); + } + } + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/MultipleValueFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/MultipleValueFilterPopupComponent.java new file mode 100644 index 000000000000..47872d4ed5dc --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/MultipleValueFilterPopupComponent.java @@ -0,0 +1,166 @@ +/* + * 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.vcs.log.ui.filter; + +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupAdapter; +import com.intellij.openapi.ui.popup.LightweightWindowEvent; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.vcs.log.VcsLogFilter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; + +abstract class MultipleValueFilterPopupComponent extends FilterPopupComponent { + + private static final int MAX_FILTER_VALUE_LENGTH = 30; + + @Nullable private Collection mySelectedValues; + + MultipleValueFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull String filterName) { + super(filterUi, filterName); + } + + @NotNull + protected abstract List> getRecentValuesFromSettings(); + + protected abstract void rememberValuesInSettings(@NotNull Collection values); + + @NotNull + protected abstract List getAllValues(); + + @Nullable + protected Collection getSelectedValues() { + return mySelectedValues; + } + + @NotNull + protected ActionGroup createRecentItemsActionGroup() { + DefaultActionGroup group = new DefaultActionGroup(); + List> recentlyFilteredUsers = getRecentValuesFromSettings(); + if (!recentlyFilteredUsers.isEmpty()) { + group.addSeparator("Recent"); + for (List recentGroup : recentlyFilteredUsers) { + group.add(new PredefinedValueAction(recentGroup)); + } + group.addSeparator(); + } + return group; + } + + private void apply(@Nullable Collection values, String text, String tooltip) { + mySelectedValues = values; + applyFilters(); + setValue(text, tooltip); + if (values != null) { + rememberValuesInSettings(values); + } + } + + @NotNull + private static String displayableText(@NotNull Collection values) { + if (values.size() == 1) { + return values.iterator().next(); + } + return StringUtil.shortenTextWithEllipsis(StringUtil.join(values, "|"), MAX_FILTER_VALUE_LENGTH, 0, true); + } + + @NotNull + private static String tooltip(@NotNull Collection values) { + return StringUtil.join(values, ", "); + } + + @Override + @NotNull + protected AnAction createAllAction() { + return new DumbAwareAction(ALL) { + @Override + public void actionPerformed(AnActionEvent e) { + apply(null, ALL, ALL); + } + }; + } + + @NotNull + protected AnAction createPredefinedValueAction(@NotNull Collection values) { + return new PredefinedValueAction(values); + } + + @NotNull + protected AnAction createSelectMultipleValuesAction() { + return new SelectMultipleValuesAction(); + } + + private class PredefinedValueAction extends DumbAwareAction { + + @NotNull private final Collection myValues; + + public PredefinedValueAction(@NotNull Collection values) { + super(displayableText(values), tooltip(values), null); + myValues = values; + } + + @Override + public void actionPerformed(AnActionEvent e) { + apply(myValues, displayableText(myValues), tooltip(myValues)); + } + } + + private class SelectMultipleValuesAction extends DumbAwareAction { + + @NotNull private final Collection myVariants; + + SelectMultipleValuesAction() { + super("Select..."); + myVariants = getAllValues(); + } + + @Override + public void actionPerformed(AnActionEvent e) { + Project project = e.getProject(); + if (project == null) { + return; + } + + final MultilinePopupBuilder popupBuilder = new MultilinePopupBuilder(project, myVariants); + JBPopup popup = popupBuilder.createPopup(); + popup.addListener(new JBPopupAdapter() { + @Override + public void onClosed(LightweightWindowEvent event) { + if (event.isOk()) { + Collection selectedValues = popupBuilder.getSelectedValues(); + if (selectedValues.isEmpty()) { + apply(null, ALL, ALL); + } + else { + apply(selectedValues, displayableText(selectedValues), tooltip(selectedValues)); + } + } + } + }); + popup.showUnderneathOf(MultipleValueFilterPopupComponent.this); + } + } + +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java index cd037437124e..60ac9f827f7c 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java @@ -15,54 +15,34 @@ */ package com.intellij.vcs.log.ui.filter; -import com.intellij.codeInsight.completion.CompletionResultSet; -import com.intellij.codeInsight.lookup.LookupElementBuilder; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.fileTypes.FileTypes; -import com.intellij.openapi.keymap.KeymapUtil; -import com.intellij.openapi.project.DumbAwareAction; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.*; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.spellchecker.ui.SpellCheckingEditorCustomization; -import com.intellij.ui.EditorCustomization; -import com.intellij.ui.EditorTextField; -import com.intellij.ui.EditorTextFieldProvider; -import com.intellij.ui.SoftWrapsEditorCustomization; import com.intellij.util.Function; -import com.intellij.util.TextFieldCompletionProviderDumbAware; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsLogUserFilter; import com.intellij.vcs.log.VcsUser; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.data.VcsLogUiProperties; -import com.intellij.vcs.log.VcsLogUserFilter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import javax.swing.border.CompoundBorder; -import java.awt.*; import java.util.*; -import java.util.List; /** * Show a popup to select a user or enter the user name. */ -class UserFilterPopupComponent extends FilterPopupComponent { +class UserFilterPopupComponent extends MultipleValueFilterPopupComponent { private static final String ME = "me"; - private static final char[] USERS_SEPARATORS = { ',', '|', '\n' }; private final VcsLogDataHolder myDataHolder; private final VcsLogUiProperties myUiProperties; - @Nullable private Collection mySelectedUsers; - - UserFilterPopupComponent(VcsLogClassicFilterUi filterUi, VcsLogDataHolder dataHolder, VcsLogUiProperties uiProperties) { + UserFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull VcsLogDataHolder dataHolder, + @NotNull VcsLogUiProperties uiProperties) { super(filterUi, "User"); myDataHolder = dataHolder; myUiProperties = uiProperties; @@ -70,197 +50,43 @@ class UserFilterPopupComponent extends FilterPopupComponent { @Override protected ActionGroup createActionGroup() { - AnAction allAction = new DumbAwareAction(ALL) { - @Override - public void actionPerformed(AnActionEvent e) { - apply(null, ALL, ALL); - } - }; - DefaultActionGroup group = new DefaultActionGroup(); - group.add(allAction); - group.add(new UserAction(Collections.singleton(ME))); - - List> recentlyFilteredUsers = myUiProperties.getRecentlyFilteredUserGroups(); - if (!recentlyFilteredUsers.isEmpty()) { - group.addSeparator("Recently searched"); - for (List recentGroup : recentlyFilteredUsers) { - group.add(new UserAction(recentGroup)); - } - } - group.addSeparator(); - group.add(new SelectUserAction()); + group.add(createAllAction()); + group.add(createPredefinedValueAction(Collections.singleton(ME))); + group.addAll(createRecentItemsActionGroup()); + group.add(createSelectMultipleValuesAction()); return group; } + @NotNull + @Override + protected List> getRecentValuesFromSettings() { + return myUiProperties.getRecentlyFilteredUserGroups(); + } + @Nullable @Override protected VcsLogUserFilter getFilter() { - if (mySelectedUsers == null) { + if (getSelectedValues() == null) { return null; } - myUiProperties.addRecentlyFilteredUserGroup(new ArrayList(mySelectedUsers)); - return new VcsLogUserFilterImpl(mySelectedUsers, myDataHolder.getCurrentUser()); + return new VcsLogUserFilterImpl(getSelectedValues(), myDataHolder.getCurrentUser()); } - private void apply(Collection users, String text, String tooltip) { - mySelectedUsers = users; - applyFilters(); - setValue(text, tooltip); + @Override + protected void rememberValuesInSettings(@NotNull Collection values) { + myUiProperties.addRecentlyFilteredUserGroup(new ArrayList(values)); } @NotNull - private static String displayableText(@NotNull Collection users) { - if (users.size() == 1) { - return users.iterator().next(); - } - return StringUtil.shortenTextWithEllipsis(StringUtil.join(users, "|"), 30, 0, true); - } - - @NotNull - private static String tooltip(@NotNull Collection users) { - return StringUtil.join(users, ", "); - } - - private class UserAction extends DumbAwareAction { - @NotNull private final Collection myUsers; - - UserAction(@NotNull Collection users) { - super(displayableText(users), tooltip(users), null); - myUsers = users; - } - - @Override - public void actionPerformed(AnActionEvent e) { - apply(myUsers, displayableText(myUsers), tooltip(myUsers)); - } - } - - private class SelectUserAction extends DumbAwareAction { - - SelectUserAction() { - super("Select..."); - } - - @Override - public void actionPerformed(AnActionEvent e) { - Project project = e.getProject(); - if (project == null) { - return; - } - - Collection users = ContainerUtil.map(myDataHolder.getAllUsers(), new Function() { - @Override - public String fun(VcsUser user) { - return user.getName(); - } - }); - - final MultilinePopupBuilder popupBuilder = new MultilinePopupBuilder(project, users); - JBPopup popup = popupBuilder.createPopup(); - popup.addListener(new JBPopupAdapter() { - @Override - public void onClosed(LightweightWindowEvent event) { - if (event.isOk()) { - final String userText = popupBuilder.getText().trim(); - Collection selectedUsers = ContainerUtil.toCollection(StringUtil.tokenize(userText, new String(USERS_SEPARATORS))); - if (selectedUsers.isEmpty()) { - apply(null, ALL, ALL); - } - else { - apply(selectedUsers, displayableText(selectedUsers), tooltip(selectedUsers)); - } - } - } - }); - popup.showUnderneathOf(UserFilterPopupComponent.this); - } - } - - private static class MultilinePopupBuilder { - private final EditorTextField myTextField; - - MultilinePopupBuilder(@NotNull Project project, @NotNull final Collection users) { - myTextField = createTextField(project); - new UsersCompletionProvider(users).apply(myTextField); - } - - @NotNull - private static EditorTextField createTextField(@NotNull Project project) { - final EditorTextFieldProvider service = ServiceManager.getService(project, EditorTextFieldProvider.class); - List features = Arrays.asList(SoftWrapsEditorCustomization.ENABLED, - SpellCheckingEditorCustomization.DISABLED); - EditorTextField textField = service.getEditorField(FileTypes.PLAIN_TEXT.getLanguage(), project, features); - textField.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2), textField.getBorder())); - textField.setOneLineMode(false); - return textField; - } - - @NotNull - JBPopup createPopup() { - JPanel panel = new JPanel(new BorderLayout()); - panel.add(myTextField, BorderLayout.CENTER); - ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, myTextField) - .setCancelOnClickOutside(true) - .setAdText(KeymapUtil.getShortcutsText(CommonShortcuts.CTRL_ENTER.getShortcuts()) + " to finish") - .setMovable(true) - .setRequestFocus(true) - .setResizable(true) - .setMayBeParent(true); - - final JBPopup popup = builder.createPopup(); - popup.setMinimumSize(new Dimension(200, 90)); - AnAction okAction = new DumbAwareAction() { - @Override - public void actionPerformed(AnActionEvent e) { - unregisterCustomShortcutSet(popup.getContent()); - popup.closeOk(e.getInputEvent()); - } - }; - okAction.registerCustomShortcutSet(CommonShortcuts.CTRL_ENTER, popup.getContent()); - return popup; - } - - String getText() { - return myTextField.getText(); - } - - private static class UsersCompletionProvider extends TextFieldCompletionProviderDumbAware { - @NotNull private final Collection myUsers; - - UsersCompletionProvider(@NotNull Collection users) { - super(true); - myUsers = users; - } - - @NotNull + @Override + protected List getAllValues() { + return ContainerUtil.map(myDataHolder.getAllUsers(), new Function() { @Override - protected String getPrefix(@NotNull String currentTextPrefix) { - final int separatorPosition = lastSeparatorPosition(currentTextPrefix); - return separatorPosition == -1 ? currentTextPrefix : currentTextPrefix.substring(separatorPosition + 1).trim(); + public String fun(VcsUser user) { + return user.getName(); } - - private static int lastSeparatorPosition(@NotNull String text) { - int lastPosition = -1; - for (char separator : USERS_SEPARATORS) { - int lio = text.lastIndexOf(separator); - if (lio > lastPosition) { - lastPosition = lio; - } - } - return lastPosition; - } - - @Override - protected void addCompletionVariants(@NotNull String text, int offset, @NotNull String prefix, - @NotNull CompletionResultSet result) { - result.addLookupAdvertisement("Select one or more users separated with comma, | or new lines"); - for (String completionVariant : myUsers) { - final LookupElementBuilder element = LookupElementBuilder.create(completionVariant); - result.addElement(element.withLookupString(completionVariant.toLowerCase())); - } - } - } + }); } private static class VcsLogUserFilterImpl implements VcsLogUserFilter { @@ -295,6 +121,5 @@ class UserFilterPopupComponent extends FilterPopupComponent { } }); } - } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java index 6d66cc7708c6..2c85927e1e08 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java @@ -26,6 +26,9 @@ import com.intellij.ui.SearchTextFieldWithStoredHistory; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.VcsLogFilterCollection; import com.intellij.vcs.log.VcsLogTextFilter; +import com.intellij.vcs.log.data.DataPack; +import com.intellij.vcs.log.data.VcsLogDataHolder; +import com.intellij.vcs.log.data.VcsLogUiProperties; import com.intellij.vcs.log.impl.VcsLogFilterCollectionImpl; import com.intellij.vcs.log.ui.VcsLogUI; import org.jetbrains.annotations.NotNull; @@ -47,7 +50,8 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { @NotNull private final DateFilterPopupComponent myDateFilterComponent; @NotNull private final StructureFilterPopupComponent myStructureFilterComponent; - public VcsLogClassicFilterUi(@NotNull VcsLogUI ui) { + public VcsLogClassicFilterUi(@NotNull VcsLogUI ui, @NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiProperties uiProperties, + @NotNull DataPack initialDataPack) { myUi = ui; myTextFilter = new SearchTextFieldWithStoredHistory("Vcs.Log.Text.Filter.History"); @@ -59,10 +63,10 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { } }); - myBranchFilterComponent = new BranchFilterPopupComponent(this, ui); - myUserFilterComponent = new UserFilterPopupComponent(this, ui.getLogDataHolder(), ui.getUiProperties()); + myBranchFilterComponent = new BranchFilterPopupComponent(this, logDataHolder, initialDataPack.getRefsModel(), uiProperties); + myUserFilterComponent = new UserFilterPopupComponent(this, logDataHolder, uiProperties); myDateFilterComponent = new DateFilterPopupComponent(this); - myStructureFilterComponent = new StructureFilterPopupComponent(this, ui.getLogDataHolder().getRoots()); + myStructureFilterComponent = new StructureFilterPopupComponent(this, logDataHolder.getRoots()); myActionGroup = new DefaultActionGroup(); myActionGroup.add(new TextFilterComponent(myTextFilter)); @@ -72,6 +76,11 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { myActionGroup.add(new FilterActionComponent(myStructureFilterComponent)); } + @Override + public void updateDataPack(@NotNull DataPack dataPack) { + myBranchFilterComponent.updateRefsModel(dataPack.getRefsModel()); + } + @Override public ActionGroup getFilterActionComponents() { return myActionGroup; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogFilterUi.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogFilterUi.java index ff43d3020cc1..a9e2e648343c 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogFilterUi.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogFilterUi.java @@ -2,6 +2,7 @@ package com.intellij.vcs.log.ui.filter; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.vcs.log.VcsLogFilterCollection; +import com.intellij.vcs.log.data.DataPack; import org.jetbrains.annotations.NotNull; /** @@ -22,4 +23,11 @@ public interface VcsLogFilterUi { @NotNull VcsLogFilterCollection getFilters(); + /** + * Informs components that the actual DataPack has been updated (e.g. due to a log refresh).
    + * Components may want to update their fields and/or rebuild. + * @param dataPack new data pack. + */ + void updateDataPack(@NotNull DataPack dataPack); + } 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 f4401af18e4d..5afedf91b560 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,7 +16,10 @@ import com.intellij.vcs.log.RefGroup; import com.intellij.vcs.log.VcsLogProvider; import com.intellij.vcs.log.VcsLogRefManager; import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.data.DataPack; +import com.intellij.vcs.log.data.RefsModel; import com.intellij.vcs.log.data.VcsLogDataHolder; +import com.intellij.vcs.log.data.VcsLogRefreshListener; import com.intellij.vcs.log.impl.SingletonRefGroup; import com.intellij.vcs.log.impl.VcsLogUtil; import com.intellij.vcs.log.ui.VcsLogUI; @@ -50,10 +53,10 @@ public class BranchesPanel extends JPanel { private Map myRefPositions = ContainerUtil.newHashMap(); - public BranchesPanel(@NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUI UI) { + public BranchesPanel(@NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUI UI, @NotNull RefsModel initialRefsModel) { myDataHolder = dataHolder; myUI = UI; - myRefGroups = getRefsToDisplayOnPanel(); + myRefGroups = getRefsToDisplayOnPanel(initialRefsModel); myRefPainter = new RefPainter(myUI.getColorManager(), true); setPreferredSize(new Dimension(-1, HEIGHT_CELL + UIUtil.DEFAULT_VGAP)); @@ -82,10 +85,10 @@ public class BranchesPanel extends JPanel { }); Project project = dataHolder.getProject(); - project.getMessageBus().connect(project).subscribe(VcsLogDataHolder.REFRESH_COMPLETED, new Runnable() { + project.getMessageBus().connect(project).subscribe(VcsLogDataHolder.REFRESH_COMPLETED, new VcsLogRefreshListener() { @Override - public void run() { - rebuild(); + public void refresh(@NotNull DataPack dataPack) { + rebuild(dataPack.getRefsModel()); } }); } @@ -116,14 +119,14 @@ public class BranchesPanel extends JPanel { } } - public void rebuild() { - myRefGroups = getRefsToDisplayOnPanel(); + public void rebuild(@NotNull RefsModel refsModel) { + myRefGroups = getRefsToDisplayOnPanel(refsModel); getParent().repaint(); } @NotNull - private List getRefsToDisplayOnPanel() { - Collection allRefs = myDataHolder.getDataPack().getRefsModel().getBranches(); + private List getRefsToDisplayOnPanel(@NotNull RefsModel refsModel) { + Collection allRefs = refsModel.getBranches(); List groups = ContainerUtil.newArrayList(); for (Map.Entry> entry : VcsLogUtil.groupRefsByRoot(allRefs).entrySet()) { 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 04b00e033b5b..31b016e2ec01 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 @@ -17,6 +17,7 @@ import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.Hash; import com.intellij.vcs.log.VcsFullCommitDetails; import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.data.DataPack; import com.intellij.vcs.log.data.LoadingDetails; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.graph.render.PrintParameters; @@ -54,10 +55,14 @@ class DetailsPanel extends JPanel implements ListSelectionListener { @NotNull private final MessagePanel myMessagePanel; @NotNull private final JBLoadingPanel myLoadingPanel; - DetailsPanel(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogGraphTable graphTable, @NotNull VcsLogColorManager colorManager) { + @NotNull private DataPack myDataPack; + + DetailsPanel(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogGraphTable graphTable, @NotNull VcsLogColorManager colorManager, + @NotNull DataPack initialDataPack) { super(new CardLayout()); myLogDataHolder = logDataHolder; myGraphTable = graphTable; + myDataPack = initialDataPack; myRefsPanel = new RefsPanel(colorManager); myDataPanel = new DataPanel(logDataHolder.getProject()); @@ -90,6 +95,10 @@ class DetailsPanel extends JPanel implements ListSelectionListener { showMessage("No commits selected"); } + void updateDataPack(@NotNull DataPack dataPack) { + myDataPack = dataPack; + } + @Override public void valueChanged(@Nullable ListSelectionEvent notUsed) { int[] rows = myGraphTable.getSelectedRows(); @@ -102,7 +111,7 @@ class DetailsPanel extends JPanel implements ListSelectionListener { else { ((CardLayout)getLayout()).show(this, STANDARD_LAYER); int row = rows[0]; - AbstractVcsLogTableModel tableModel = (AbstractVcsLogTableModel)myGraphTable.getModel(); + AbstractVcsLogTableModel tableModel = (AbstractVcsLogTableModel)myGraphTable.getModel(); Hash hash = tableModel.getHashAtRow(row); VcsFullCommitDetails commitData = myLogDataHolder.getCommitDetailsGetter().getCommitData(row, tableModel); if (commitData == null || hash == null) { @@ -136,7 +145,7 @@ class DetailsPanel extends JPanel implements ListSelectionListener { @NotNull private List sortRefs(@NotNull Hash hash, @NotNull VirtualFile root) { - Collection refs = myLogDataHolder.getDataPack().getRefsModel().refsToCommit(hash); + Collection refs = myDataPack.getRefsModel().refsToCommit(hash); return myLogDataHolder.getLogProvider(root).getReferenceManager().sort(refs); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java index 7dbd145c201e..eea8332d5425 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java @@ -19,6 +19,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.vcs.log.VcsLog; import com.intellij.vcs.log.VcsLogDataKeys; import com.intellij.vcs.log.VcsLogSettings; +import com.intellij.vcs.log.data.DataPack; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.data.VcsLogUiProperties; import com.intellij.vcs.log.ui.VcsLogUI; @@ -36,9 +37,6 @@ import java.awt.*; import java.util.Collections; import java.util.List; -/** - * @author erokhins - */ public class MainFrame extends JPanel implements TypeSafeDataProvider { @NotNull private final VcsLogDataHolder myLogDataHolder; @@ -56,20 +54,21 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider { private final JComponent myToolbar; public MainFrame(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUI vcsLogUI, @NotNull Project project, - @NotNull VcsLogSettings settings, @NotNull VcsLogUiProperties uiProperties, @NotNull VcsLog log) { + @NotNull VcsLogSettings settings, @NotNull VcsLogUiProperties uiProperties, @NotNull VcsLog log, + @NotNull DataPack initialDataPack) { // collect info myLogDataHolder = logDataHolder; myUI = vcsLogUI; myProject = project; myUiProperties = uiProperties; myLog = log; - myFilterUi = new VcsLogClassicFilterUi(myUI); + myFilterUi = new VcsLogClassicFilterUi(myUI, logDataHolder, uiProperties, initialDataPack); // initialize components - myGraphTable = new VcsLogGraphTable(vcsLogUI, logDataHolder); - myBranchesPanel = new BranchesPanel(logDataHolder, vcsLogUI); + myGraphTable = new VcsLogGraphTable(vcsLogUI, logDataHolder, initialDataPack); + myBranchesPanel = new BranchesPanel(logDataHolder, vcsLogUI, initialDataPack.getRefsModel()); myBranchesPanel.setVisible(settings.isShowBranchesPanel()); - myDetailsPanel = new DetailsPanel(logDataHolder, myGraphTable, vcsLogUI.getColorManager()); + myDetailsPanel = new DetailsPanel(logDataHolder, myGraphTable, vcsLogUI.getColorManager(), initialDataPack); final ChangesBrowser changesBrowser = new RepositoryChangesBrowser(project, null, Collections.emptyList(), null); changesBrowser.getDiffAction().registerCustomShortcutSet(CommonShortcuts.getDiff(), getGraphTable()); @@ -111,6 +110,17 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider { }); } + /** + * Informs components that the actual DataPack has been updated (e.g. due to a log refresh).
    + * Components may want to update their fields and/or rebuild. + * @param dataPack new data pack. + */ + public void updateDataPack(@NotNull DataPack dataPack) { + myFilterUi.updateDataPack(dataPack); + myDetailsPanel.updateDataPack(dataPack); + myGraphTable.updateDataPack(dataPack); + } + private void updateWhenDetailsAreLoaded(final CommitSelectionListener selectionChangeListener) { myLogDataHolder.getMiniDetailsGetter().addDetailsLoadedListener(new Runnable() { @Override 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 3532137577d3..2423dacb1fc5 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 @@ -2,7 +2,6 @@ package com.intellij.vcs.log.ui.frame; import com.intellij.ide.CopyProvider; import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.changes.Change; @@ -12,17 +11,17 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.JBColor; import com.intellij.ui.PopupHandler; import com.intellij.ui.table.JBTable; +import com.intellij.util.PairFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.Hash; +import com.intellij.vcs.log.data.DataPack; import com.intellij.vcs.log.data.VcsLogDataHolder; -import com.intellij.vcs.log.graph.elements.Edge; -import com.intellij.vcs.log.graph.elements.GraphElement; -import com.intellij.vcs.log.graph.elements.Node; -import com.intellij.vcs.log.graph.render.*; -import com.intellij.vcs.log.printmodel.GraphPrintCell; -import com.intellij.vcs.log.printmodel.SpecialPrintElement; +import com.intellij.vcs.log.graph.*; +import com.intellij.vcs.log.graph.render.CommitCell; +import com.intellij.vcs.log.graph.render.GraphCommitCell; +import com.intellij.vcs.log.graph.render.PositionUtil; import com.intellij.vcs.log.ui.VcsLogUI; import com.intellij.vcs.log.ui.render.CommitCellRender; import com.intellij.vcs.log.ui.render.GraphCommitCellRender; @@ -31,7 +30,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.event.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.table.*; import java.awt.*; import java.awt.datatransfer.StringSelection; @@ -43,30 +43,32 @@ import java.util.List; import static com.intellij.vcs.log.graph.render.PrintParameters.HEIGHT_CELL; -/** - * @author erokhins - */ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, CopyProvider { - private static final Logger LOG = Logger.getInstance(VcsLogGraphTable.class); private static final int ROOT_INDICATOR_WIDTH = 5; private static final int MAX_DEFAULT_AUTHOR_COLUMN_WIDTH = 200; + private static final int MAX_ROWS_TO_CALC_WIDTH = 1000; + private static final int MAX_ROWS_TO_CALC_OFFSET = 100; @NotNull private final VcsLogUI myUI; - @NotNull private final GraphCellPainter myGraphPainter; + private final VcsLogDataHolder myLogDataHolder; + private final GraphCommitCellRender myGraphCommitCellRender; private boolean myColumnsSizeInitialized = false; private volatile boolean myRepaintFreezed; - public VcsLogGraphTable(@NotNull VcsLogUI UI, final VcsLogDataHolder logDataHolder) { + @NotNull private DataPack myDataPack; + + public VcsLogGraphTable(@NotNull VcsLogUI UI, @NotNull final VcsLogDataHolder logDataHolder, @NotNull DataPack initialDataPack) { super(); myUI = UI; - - myGraphPainter = new SimpleGraphCellPainter(logDataHolder.isMultiRoot()); + myLogDataHolder = logDataHolder; + myDataPack = initialDataPack; + myGraphCommitCellRender = new GraphCommitCellRender(myUI.getColorManager(), logDataHolder, myDataPack.getGraphFacade()); setDefaultRenderer(VirtualFile.class, new RootCellRenderer(myUI)); - setDefaultRenderer(GraphCommitCell.class, new GraphCommitCellRender(myGraphPainter, logDataHolder, myUI.getColorManager())); - setDefaultRenderer(CommitCell.class, new CommitCellRender(myUI.getColorManager(), logDataHolder.getProject())); + setDefaultRenderer(GraphCommitCell.class, myGraphCommitCellRender); + setDefaultRenderer(CommitCell.class, new CommitCellRender(myUI.getColorManager(), logDataHolder)); setDefaultRenderer(String.class, new StringCellRenderer()); setRowHeight(HEIGHT_CELL); @@ -88,31 +90,6 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C addMouseListener(mouseAdapter); PopupHandler.installPopupHandler(this, VcsLogUI.POPUP_ACTION_GROUP, VcsLogUI.VCS_LOG_TABLE_PLACE); - - getColumnModel().addColumnModelListener(new TableColumnModelListener() { - @Override - public void columnAdded(TableColumnModelEvent e) { - if (e.getToIndex() == AbstractVcsLogTableModel.ROOT_COLUMN) { - myGraphPainter.setRootColumn(getColumnModel().getColumn(AbstractVcsLogTableModel.ROOT_COLUMN)); - } - } - - @Override - public void columnRemoved(TableColumnModelEvent e) { - } - - @Override - public void columnMoved(TableColumnModelEvent e) { - } - - @Override - public void columnMarginChanged(ChangeEvent e) { - } - - @Override - public void columnSelectionChanged(ListSelectionEvent e) { - } - }); } @Override @@ -140,7 +117,12 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C column.setPreferredWidth(Short.MAX_VALUE); } else if (i == AbstractVcsLogTableModel.AUTHOR_COLUMN) { // detect author with the longest name - int contentWidth = calcMaxContentColumnWidth(i, 1000); + // to avoid querying the last row (it would lead to full graph loading) + int maxRowsToCheck = Math.min(MAX_ROWS_TO_CALC_WIDTH, getRowCount() - MAX_ROWS_TO_CALC_OFFSET); + if (maxRowsToCheck < 0) { // but if the log is small, check all of them + maxRowsToCheck = getRowCount(); + } + int contentWidth = calcMaxContentColumnWidth(i, maxRowsToCheck); column.setMinWidth(Math.min(contentWidth, MAX_DEFAULT_AUTHOR_COLUMN_WIDTH)); column.setWidth(column.getMinWidth()); } @@ -154,7 +136,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C private int calcMaxContentColumnWidth(int columnIndex, int maxRowsToCheck) { int maxWidth = 0; - for (int row = 0; row < maxRowsToCheck && row < getRowCount(); row++) { + for (int row = 0; row < maxRowsToCheck; row++) { TableCellRenderer renderer = getCellRenderer(row, columnIndex); Component comp = prepareRenderer(renderer, row, columnIndex); maxWidth = Math.max(comp.getPreferredSize().width, maxWidth); @@ -205,19 +187,6 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C } } - @Nullable - public GraphPrintCell getGraphPrintCellForRow(TableModel model, int rowIndex) { - if (rowIndex >= model.getRowCount()) { - return null; - } - Object commitValue = model.getValueAt(rowIndex, AbstractVcsLogTableModel.COMMIT_COLUMN); - if (commitValue instanceof GraphCommitCell) { - GraphCommitCell commitCell = (GraphCommitCell)commitValue; - return commitCell.getPrintCell(); - } - return null; - } - @Nullable public List getSelectedChanges() { TableModel model = getModel(); @@ -269,84 +238,59 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C return true; } + public void updateDataPack(@NotNull DataPack dataPack) { + myDataPack = dataPack; + myGraphCommitCellRender.updateGraphFacade(dataPack.getGraphFacade()); + } + private class MyMouseAdapter extends MouseAdapter { - private final Cursor DEFAULT_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR); - private final Cursor HAND_CURSOR = new Cursor(Cursor.HAND_CURSOR); - private final TableLinkMouseListener myTableListener; + private final TableLinkMouseListener myLinkListener; MyMouseAdapter() { - myTableListener = new TableLinkMouseListener(); - } - - @Nullable - private GraphPrintCell getGraphPrintCell(MouseEvent e) { - int rowIndex = PositionUtil.getRowIndex(e); - return getGraphPrintCellForRow(getModel(), rowIndex); - } - - @Nullable - private GraphElement overCell(MouseEvent e) { - int y = PositionUtil.getYInsideRow(e); - int x = e.getX(); - GraphPrintCell row = getGraphPrintCell(e); - return row != null ? myGraphPainter.mouseOver(row, x, y) : null; - } - - @Nullable - private Node arrowToNode(MouseEvent e) { - int y = PositionUtil.getYInsideRow(e); - int x = e.getX(); - GraphPrintCell row = getGraphPrintCell(e); - if (row == null) { - return null; - } - SpecialPrintElement printElement = myGraphPainter.mouseOverArrow(row, x, y); - if (printElement == null) { - return null; - } - Edge edge = printElement.getGraphElement().getEdge(); - if (edge == null) { - return null; - } - return printElement.getType() == SpecialPrintElement.Type.DOWN_ARROW ? edge.getDownNode() : edge.getUpNode(); + myLinkListener = new TableLinkMouseListener(); } @Override public void mouseClicked(MouseEvent e) { + if (myLinkListener.onClick(e, e.getClickCount())) { + return; + } + if (e.getClickCount() == 1) { - Node jumpToNode = arrowToNode(e); - if (jumpToNode != null) { - jumpToRow(jumpToNode.getRowIndex()); - return; - } - GraphElement graphElement = overCell(e); - myUI.click(graphElement); - if (graphElement == null) { - myUI.click(PositionUtil.getRowIndex(e)); - } + performAction(e, new PairFunction() { + @Override + public GraphAction fun(Integer row, Point point) { + return new ClickGraphAction(row, point); + } + }); } - myTableListener.onClick(e, e.getClickCount()); } @Override public void mouseMoved(MouseEvent e) { - Node jumpToNode = arrowToNode(e); - if (jumpToNode != null || isAboveLink(e)) { - setCursor(HAND_CURSOR); + if (isAboveLink(e)) { + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { - setCursor(DEFAULT_CURSOR); - } - - if (jumpToNode == null) { - myUI.over(overCell(e)); - } else { - myUI.over(null); + performAction(e, new PairFunction() { + @Override + public GraphAction fun(Integer row, Point point) { + return new MouseOverAction(row, point); + } + }); } } + private void performAction(@NotNull MouseEvent e, @NotNull PairFunction actionConstructor) { + int row = PositionUtil.getRowIndex(e.getPoint()); + Point point = calcPoint4Graph(e.getPoint()); + GraphFacade graphFacade = myDataPack.getGraphFacade(); + GraphAnswer answer = graphFacade.performAction(actionConstructor.fun(row, point)); + myUI.handleAnswer(answer); + } + private boolean isAboveLink(MouseEvent e) { - return myTableListener.getTagAt(e) != null; + return myLinkListener.getTagAt(e) != null; } @Override @@ -360,9 +304,17 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C } } - private static class RootCellRenderer extends JPanel implements TableCellRenderer { + @NotNull + private Point calcPoint4Graph(@NotNull Point clickPoint) { + return new Point(clickPoint.x - getXOffset(), PositionUtil.getYInsideRow(clickPoint)); + } - private static final Logger LOG = Logger.getInstance(RootCellRenderer.class); + private int getXOffset() { + TableColumn rootColumn = getColumnModel().getColumn(AbstractVcsLogTableModel.ROOT_COLUMN); + return myLogDataHolder.isMultiRoot() ? rootColumn.getWidth() : 0; + } + + private static class RootCellRenderer extends JPanel implements TableCellRenderer { @NotNull private final VcsLogUI myUi; @@ -396,10 +348,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component rendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - Object commit = getValueAt(row, AbstractVcsLogTableModel.COMMIT_COLUMN); - if (commit instanceof GraphCommitCell) { - setBackground(isSelected ? table.getSelectionBackground() : JBColor.WHITE); - } + setBackground(isSelected ? table.getSelectionBackground() : JBColor.WHITE); return rendererComponent; } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/AbstractPaddingCellRender.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/AbstractPaddingCellRender.java index d75ca1d6624f..b988ccd93edc 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/AbstractPaddingCellRender.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/AbstractPaddingCellRender.java @@ -1,13 +1,16 @@ package com.intellij.vcs.log.ui.render; -import com.intellij.openapi.project.Project; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.ColoredTableCellRenderer; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.VcsRef; -import com.intellij.vcs.log.graph.render.GraphCommitCell; -import com.intellij.vcs.log.printmodel.SpecialPrintElement; +import com.intellij.vcs.log.data.VcsLogDataHolder; +import com.intellij.vcs.log.graph.PaintInfo; +import com.intellij.vcs.log.graph.render.CommitCell; import com.intellij.vcs.log.ui.VcsLogColorManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -15,57 +18,78 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; public abstract class AbstractPaddingCellRender extends ColoredTableCellRenderer { - public static final Color MARKED_BACKGROUND = new Color(200, 255, 250); + private static final Logger LOG = Logger.getInstance(AbstractPaddingCellRender.class); - @NotNull protected final RefPainter myRefPainter; + @NotNull private final VcsLogDataHolder myDataHolder; + + @NotNull private final RefPainter myRefPainter; @NotNull private final IssueLinkRenderer myIssueLinkRenderer; - @Nullable private Object myValue; - protected AbstractPaddingCellRender(@NotNull Project project, VcsLogColorManager colorManager) { + @Nullable private PaintInfo myGraphImage; + @Nullable private Collection myRefs; + + protected AbstractPaddingCellRender(@NotNull VcsLogColorManager colorManager, @NotNull VcsLogDataHolder dataHolder) { + myDataHolder = dataHolder; myRefPainter = new RefPainter(colorManager, false); - myIssueLinkRenderer = new IssueLinkRenderer(project, this); + myIssueLinkRenderer = new IssueLinkRenderer(dataHolder.getProject(), this); } - protected abstract int getLeftPadding(JTable table, Object value); - - @NotNull - protected abstract String getCellText(@Nullable Object value); - - protected abstract void additionPaint(Graphics g, Object value); - - public static boolean isMarked(@Nullable Object value) { - if (!(value instanceof GraphCommitCell)) { - return false; - } - GraphCommitCell cell = (GraphCommitCell)value; - for (SpecialPrintElement printElement : cell.getPrintCell().getSpecialPrintElements()) { - if (printElement.getType() == SpecialPrintElement.Type.COMMIT_NODE && printElement.isMarked()) { - return true; - } - } - return false; - } + @Nullable + protected abstract PaintInfo getGraphImage(int row); @Override protected void customizeCellRenderer(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value == null) { return; } - myValue = value; + + CommitCell cell = getAssertCommitCell(value); + myGraphImage = getGraphImage(row); + myRefs = cell.getRefsToThisCommit(); + + int graphPadding; + if (myGraphImage != null) { + graphPadding = myGraphImage.getWidth(); + if (graphPadding < 2) { // TODO temporary diagnostics: why does graph sometimes disappear + LOG.error("Too small image width: " + graphPadding); + } + } + else { + graphPadding = 0; + } + int textPadding = graphPadding + calcRefsPadding(myRefs); + append(""); - appendFixedTextFragmentWidth(getLeftPadding(table, value)); - myIssueLinkRenderer.appendTextWithLinks(getCellText(value)); + appendFixedTextFragmentWidth(textPadding); + myIssueLinkRenderer.appendTextWithLinks(cell.getText()); } @Override - public void paint(Graphics g) { - super.paint(g); - additionPaint(g, myValue); + public void paintComponent(Graphics g) { + super.paintComponent(g); + + if (myRefs != null) { + int graphPadding = myGraphImage != null ? myGraphImage.getWidth() : 0; + drawRefs((Graphics2D)g, myRefs, graphPadding); + } + + if (myGraphImage != null) { + UIUtil.drawImage(g, myGraphImage.getImage(), 0, 0, null); + } + else if (this instanceof GraphCommitCellRender) { // TODO temporary diagnostics: why does graph sometimes disappear + LOG.error("Image is null"); + } + } + + private static CommitCell getAssertCommitCell(Object value) { + assert value instanceof CommitCell : "Value of incorrect class was supplied: " + value; + return (CommitCell)value; } protected void drawRefs(@NotNull Graphics2D g2, @NotNull Collection refs, int padding) { @@ -73,14 +97,19 @@ public abstract class AbstractPaddingCellRender extends ColoredTableCellRenderer } @NotNull - private static Map collectLabelsForRefs(@NotNull Collection refs) { + private Map collectLabelsForRefs(@NotNull Collection refs) { + if (refs.isEmpty()) { + return Collections.emptyMap(); + } + VirtualFile root = refs.iterator().next().getRoot(); // all refs are from the same commit => they have the same root + refs = myDataHolder.getLogProvider(root).getReferenceManager().sort(refs); List branches = getBranches(refs); Collection tags = ContainerUtil.subtract(refs, branches); return getLabelsForRefs(branches, tags); } - protected int calcRefsPadding(@NotNull Collection refs, @NotNull Graphics2D g2) { - return myRefPainter.padding(collectLabelsForRefs(refs).keySet(), g2); + protected int calcRefsPadding(@NotNull Collection refs) { + return myRefPainter.padding(collectLabelsForRefs(refs).keySet(), this.getFontMetrics(RefPainter.DEFAULT_FONT)); } @NotNull diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/CommitCellRender.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/CommitCellRender.java index 702af3f1a667..cb9040bc6e22 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/CommitCellRender.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/CommitCellRender.java @@ -1,48 +1,21 @@ package com.intellij.vcs.log.ui.render; -import com.intellij.openapi.project.Project; -import com.intellij.vcs.log.graph.render.CommitCell; +import com.intellij.vcs.log.data.VcsLogDataHolder; +import com.intellij.vcs.log.graph.PaintInfo; import com.intellij.vcs.log.ui.VcsLogColorManager; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import java.awt.*; - -/** - * @author erokhins - */ public class CommitCellRender extends AbstractPaddingCellRender { - public CommitCellRender(@NotNull VcsLogColorManager colorManager, Project project) { - super(project, colorManager); + public CommitCellRender(@NotNull VcsLogColorManager colorManager, @NotNull VcsLogDataHolder dataHolder) { + super(colorManager, dataHolder); } + @Nullable @Override - protected int getLeftPadding(JTable table, Object value) { - CommitCell cell = getAssertCommitCell(value); - return calcRefsPadding(cell.getRefsToThisCommit(), (Graphics2D)table.getGraphics()); - } - - private static CommitCell getAssertCommitCell(Object value) { - assert value instanceof CommitCell : "Value of incorrect class was supplied: " + value; - return (CommitCell)value; - } - - @NotNull - @Override - protected String getCellText(Object value) { - if (value == null) { - return ""; - } - CommitCell cell = getAssertCommitCell(value); - return cell.getText(); - } - - @Override - protected void additionPaint(Graphics g, Object value) { - CommitCell cell = getAssertCommitCell(value); - Graphics2D g2 = (Graphics2D)g; - drawRefs(g2, cell.getRefsToThisCommit(), 0); + protected PaintInfo getGraphImage(int row) { + return null; } } 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 12c7600b770d..4448cd499977 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 @@ -15,91 +15,31 @@ */ package com.intellij.vcs.log.ui.render; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ui.UIUtil; -import com.intellij.vcs.log.VcsRef; import com.intellij.vcs.log.data.VcsLogDataHolder; -import com.intellij.vcs.log.graph.render.GraphCellPainter; -import com.intellij.vcs.log.graph.render.GraphCommitCell; +import com.intellij.vcs.log.graph.GraphFacade; +import com.intellij.vcs.log.graph.PaintInfo; import com.intellij.vcs.log.ui.VcsLogColorManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.util.Collection; - -import static com.intellij.vcs.log.graph.render.PrintParameters.HEIGHT_CELL; -import static com.intellij.vcs.log.graph.render.PrintParameters.WIDTH_NODE; - -/** - * @author erokhins - */ public class GraphCommitCellRender extends AbstractPaddingCellRender { - // In case of diagonal edges, one node can be at most 3 "arrows" + 2 nodes at the left from another - that is enough for sure - private static final int IMAGE_WIDTH_RESERVE = 5 * WIDTH_NODE; + @NotNull private GraphFacade myGraphFacade; - @NotNull private final GraphCellPainter graphPainter; - @NotNull private final VcsLogDataHolder myDataHolder; - - public GraphCommitCellRender(@NotNull GraphCellPainter graphPainter, @NotNull VcsLogDataHolder logDataHolder, - @NotNull VcsLogColorManager colorManager) { - super(logDataHolder.getProject(), colorManager); - this.graphPainter = graphPainter; - myDataHolder = logDataHolder; + public GraphCommitCellRender(@NotNull VcsLogColorManager colorManager, + @NotNull VcsLogDataHolder dataHolder, @NotNull GraphFacade graphFacade) { + super(colorManager, dataHolder); + myGraphFacade = graphFacade; } + @Nullable @Override - protected int getLeftPadding(JTable table, @Nullable Object value) { - GraphCommitCell cell = (GraphCommitCell)value; - if (cell == null) { - return 0; - } - return calcPaddingBeforeText(cell, (Graphics2D)table.getGraphics()); + protected PaintInfo getGraphImage(int row) { + return myGraphFacade.paint(row); } - private int calcPaddingBeforeText(GraphCommitCell cell, Graphics2D g) { - int refPadding = calcRefsPadding(cell.getRefsToThisCommit(), g); - int countCells = cell.getPrintCell().countCell(); - int graphPadding = countCells * WIDTH_NODE; - return refPadding + graphPadding; + public void updateGraphFacade(@NotNull GraphFacade graphFacade) { + myGraphFacade = graphFacade; } - @NotNull - protected String getCellText(@Nullable Object value) { - GraphCommitCell cell = (GraphCommitCell)value; - if (cell == null) { - return ""; - } - else { - return cell.getText(); - } - } - - @Override - protected void additionPaint(Graphics g, @Nullable Object value) { - GraphCommitCell cell = (GraphCommitCell)value; - if (cell == null) { - return; - } - - int width = calcPaddingBeforeText(cell, (Graphics2D)g) + IMAGE_WIDTH_RESERVE; - BufferedImage image = UIUtil.createImage(width, HEIGHT_CELL, BufferedImage.TYPE_INT_ARGB); - Graphics2D g2 = image.createGraphics(); - - graphPainter.draw(g2, cell.getPrintCell()); - - int countCells = cell.getPrintCell().countCell(); - int padding = countCells * WIDTH_NODE; - Collection refs = cell.getRefsToThisCommit(); - if (!refs.isEmpty()) { - VirtualFile root = refs.iterator().next().getRoot(); // all refs are from the same commit => they have the same root - refs = myDataHolder.getLogProvider(root).getReferenceManager().sort(refs); - } - drawRefs(g2, refs, padding); - - UIUtil.drawImage(g, image, 0, 0, null); - } } 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 index 004a9eda8c17..7d555429cb91 100644 --- 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 @@ -30,7 +30,7 @@ public class RefPainter { private static final int ROUND_RADIUS = 5; - private static final Font DEFAULT_FONT = new Font("Arial", Font.PLAIN, 12); + public static final Font DEFAULT_FONT = new Font("Arial", Font.PLAIN, 12); private static final Color DEFAULT_FONT_COLOR = JBColor.BLACK; @NotNull private final VcsLogColorManager myColorManager; @@ -85,10 +85,10 @@ public class RefPainter { g2.drawPolygon(polygon); } - public int padding(@NotNull Collection refs, @NotNull Graphics2D g2) { + public int padding(@NotNull Collection refs, FontMetrics fontMetrics) { int p = 0; for (String ref : refs) { - XAndWidth xAndWidth = getXAndWidth(ref, 0, g2.getFontMetrics(DEFAULT_FONT)); + XAndWidth xAndWidth = getXAndWidth(ref, 0, fontMetrics); p += xAndWidth.x + xAndWidth.width + LABEL_PADDING; } if (p > 0) { // additional padding after all references looks better diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/AbstractVcsLogTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/AbstractVcsLogTableModel.java index aba0808a0117..ace90a0c6090 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/AbstractVcsLogTableModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/AbstractVcsLogTableModel.java @@ -8,19 +8,20 @@ import com.intellij.util.text.DateFormatUtil; import com.intellij.vcs.log.Hash; import com.intellij.vcs.log.VcsFullCommitDetails; import com.intellij.vcs.log.VcsShortCommitDetails; -import com.intellij.vcs.log.graph.elements.Node; +import com.intellij.vcs.log.data.DataPack; +import com.intellij.vcs.log.data.LoadMoreStage; +import com.intellij.vcs.log.data.LoadingDetails; +import com.intellij.vcs.log.data.VcsLogDataHolder; +import com.intellij.vcs.log.ui.VcsLogUI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.table.AbstractTableModel; +import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; -/** - * @param commit column class - * @param Commit identifier, which can be different depending on the model nature, - * for example, a {@link Hash} or an {@link Integer} or a {@link Node}. - */ -public abstract class AbstractVcsLogTableModel extends AbstractTableModel { +public abstract class AbstractVcsLogTableModel extends AbstractTableModel { public static final VirtualFile FAKE_ROOT = NullVirtualFile.INSTANCE; @@ -32,16 +33,48 @@ public abstract class AbstractVcsLogTableModel exte private static final String[] COLUMN_NAMES = {"", "Subject", "Author", "Date"}; + @NotNull private final VcsLogDataHolder myLogDataHolder; + @NotNull protected final VcsLogUI myUi; + @NotNull protected final DataPack myDataPack; + @NotNull private final LoadMoreStage myLoadMoreStage; + + @NotNull private final AtomicBoolean myLoadMoreWasRequested = new AtomicBoolean(); + + + protected AbstractVcsLogTableModel(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUI ui, @NotNull DataPack dataPack, + @NotNull LoadMoreStage loadMoreStage) { + myLogDataHolder = logDataHolder; + myUi = ui; + myDataPack = dataPack; + myLoadMoreStage = loadMoreStage; + } + @Override public final int getColumnCount() { return COLUMN_COUNT; } @Nullable - protected abstract VcsShortCommitDetails getShortDetails(int rowIndex); + protected VcsShortCommitDetails getShortDetails(int rowIndex) { + return myLogDataHolder.getMiniDetailsGetter().getCommitData(rowIndex, this); + } @Nullable - public abstract VcsFullCommitDetails getFullCommitDetails(int row); + public VcsFullCommitDetails getFullCommitDetails(int rowIndex) { + return myLogDataHolder.getCommitDetailsGetter().getCommitData(rowIndex, this); + } + + /** + * Requests the proper data provider to load more data from the log & recreate the model. + * @param onLoaded will be called upon task completion on the EDT. + */ + public void requestToLoadMore(@NotNull Runnable onLoaded) { + if (myLoadMoreWasRequested.compareAndSet(false, true) // Don't send the request to VCS twice + && myLoadMoreStage != LoadMoreStage.ALL_REQUESTED) { // or when everything possible is loaded + myUi.getTable().setPaintBusy(true); + myUi.getFilterer().requestVcs(myDataPack, myUi.collectFilters(), myLoadMoreStage, onLoaded); + } + } @NotNull @Override @@ -75,16 +108,12 @@ public abstract class AbstractVcsLogTableModel exte } } - /** - * Requests the proper data provider to load more data from the log & recreate the model. - * @param onLoaded will be called upon task completion on the EDT. - */ - public abstract void requestToLoadMore(@NotNull Runnable onLoaded); - /** * Returns true if not all data has been loaded, i.e. there is sense to {@link #requestToLoadMore(Runnable) request more data}. */ - public abstract boolean canRequestMore(); + public boolean canRequestMore() { + return !myUi.collectFilters().isEmpty() && myLoadMoreStage != LoadMoreStage.ALL_REQUESTED; + } /** * Returns Changes for commits at selected rows.
    @@ -93,7 +122,17 @@ public abstract class AbstractVcsLogTableModel exte * @return Changes selected in all rows, or null if this data is not ready yet. */ @Nullable - public abstract List getSelectedChanges(@NotNull List selectedRows); + public List getSelectedChanges(@NotNull List selectedRows) { + List changes = new ArrayList(); + for (int row : selectedRows) { + VcsFullCommitDetails commitData = getFullCommitDetails(row); + if (commitData == null || commitData instanceof LoadingDetails) { + return null; + } + changes.addAll(commitData.getChanges()); + } + return changes; + } @NotNull public abstract VirtualFile getRoot(int rowIndex); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/EmptyTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/EmptyTableModel.java new file mode 100644 index 000000000000..00ad86a27cbf --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/EmptyTableModel.java @@ -0,0 +1,78 @@ +/* + * 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.vcs.log.ui.tables; + +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.vcs.log.Hash; +import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.VcsShortCommitDetails; +import com.intellij.vcs.log.data.DataPack; +import com.intellij.vcs.log.data.LoadMoreStage; +import com.intellij.vcs.log.data.VcsLogDataHolder; +import com.intellij.vcs.log.graph.render.GraphCommitCell; +import com.intellij.vcs.log.ui.VcsLogUI; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; + +public class EmptyTableModel extends AbstractVcsLogTableModel { + + public EmptyTableModel(@NotNull DataPack dataPack, @NotNull VcsLogDataHolder logDataHolder, + @NotNull VcsLogUI ui, @NotNull LoadMoreStage loadMoreStage) { + super(logDataHolder, ui, dataPack, loadMoreStage); + } + + @Override + public int getRowCount() { + return 0; + } + + @NotNull + @Override + public VirtualFile getRoot(int rowIndex) { + return FAKE_ROOT; + } + + @NotNull + @Override + protected GraphCommitCell getCommitColumnCell(int index, @Nullable VcsShortCommitDetails details) { + return new GraphCommitCell("", Collections.emptyList()); + } + + @NotNull + @Override + protected Class getCommitColumnClass() { + return GraphCommitCell.class; + } + + @Nullable + @Override + public Hash getHashAtRow(int row) { + return null; + } + + @Override + public int getRowOfCommit(@NotNull Hash hash) { + return -1; + } + + @Override + public int getRowOfCommitByPartOfHash(@NotNull String hash) { + return -1; + } + +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java index 2b48965e2686..4be4d59beb36 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java @@ -1,114 +1,81 @@ package com.intellij.vcs.log.ui.tables; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.Hash; -import com.intellij.vcs.log.VcsFullCommitDetails; import com.intellij.vcs.log.VcsRef; import com.intellij.vcs.log.VcsShortCommitDetails; import com.intellij.vcs.log.data.DataPack; -import com.intellij.vcs.log.data.LoadingDetails; +import com.intellij.vcs.log.data.LoadMoreStage; import com.intellij.vcs.log.data.VcsLogDataHolder; -import com.intellij.vcs.log.graph.elements.Node; import com.intellij.vcs.log.graph.render.GraphCommitCell; -import com.intellij.vcs.log.printmodel.GraphPrintCell; +import com.intellij.vcs.log.impl.VcsLogUtil; import com.intellij.vcs.log.ui.VcsLogUI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; -/** - * @author Kirill Likhodedov - */ -public class GraphTableModel extends AbstractVcsLogTableModel { +public class GraphTableModel extends AbstractVcsLogTableModel { private static final Logger LOG = Logger.getInstance(GraphTableModel.class); @NotNull private final DataPack myDataPack; @NotNull private final VcsLogDataHolder myDataHolder; - @NotNull private final VcsLogUI myUi; - public GraphTableModel(@NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUI ui) { + public GraphTableModel(@NotNull DataPack dataPack, @NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUI UI, + @NotNull LoadMoreStage loadMoreStage) { + super(dataHolder, UI, dataPack, loadMoreStage); + myDataPack = dataPack; myDataHolder = dataHolder; - myUi = ui; - myDataPack = dataHolder.getDataPack(); } @Override public int getRowCount() { - return myDataPack.getGraphModel().getGraph().getNodeRows().size(); - } - - @Nullable - @Override - protected VcsShortCommitDetails getShortDetails(int rowIndex) { - return myDataHolder.getMiniDetailsGetter().getCommitData(rowIndex, this); - } - - @Nullable - @Override - public VcsFullCommitDetails getFullCommitDetails(int row) { - return myDataHolder.getCommitDetailsGetter().getCommitData(row, this); + return myDataPack.getGraphFacade().getVisibleCommitCount(); } @Override public void requestToLoadMore(@NotNull Runnable onLoaded) { - myDataHolder.showFullLog(onLoaded); + if (!myDataHolder.isFullLogShowing()) { + myDataHolder.showFullLog(onLoaded); + } + else if (!myUi.collectFilters().isEmpty()) { + super.requestToLoadMore(onLoaded); + } } @Override public boolean canRequestMore() { - return !myDataHolder.isFullLogShowing(); - } - - @Nullable - @Override - public List getSelectedChanges(@NotNull List selectedRows) { - List changes = new ArrayList(); - for (int row : selectedRows) { - VcsFullCommitDetails commitData = myDataHolder.getCommitDetailsGetter().getCommitData(row, this); - if (commitData == null || commitData instanceof LoadingDetails) { - return null; - } - changes.addAll(commitData.getChanges()); - } - return changes; - } - - @Nullable - private GraphPrintCell getGraphPrintCellForRow(int row) { - Object commitValue = getValueAt(row, AbstractVcsLogTableModel.COMMIT_COLUMN); - if (commitValue instanceof GraphCommitCell) { - GraphCommitCell commitCell = (GraphCommitCell)commitValue; - return commitCell.getPrintCell(); - } - return null; + return !myDataHolder.isFullLogShowing() || super.canRequestMore(); } @NotNull @Override public VirtualFile getRoot(int rowIndex) { - Node commitNode = myDataPack.getGraphModel().getGraph().getCommitNodeInRow(rowIndex); - return commitNode != null ? commitNode.getBranch().getRepositoryRoot() : FAKE_ROOT; + int head = myDataPack.getGraphFacade().getInfoProvider().getRowInfo(rowIndex).getOneOfHeads(); + Collection refs = myDataPack.getRefsModel().refsToCommit(head); + if (refs.isEmpty()) { + LOG.error("No references pointing to head " + head + " identified for commit at row " + rowIndex); + return FAKE_ROOT; + } + return refs.iterator().next().getRoot(); } @NotNull @Override protected GraphCommitCell getCommitColumnCell(int rowIndex, @Nullable VcsShortCommitDetails details) { - GraphPrintCell graphPrintCell = myDataPack.getPrintCellModel().getGraphPrintCell(rowIndex); String message = ""; List refs = Collections.emptyList(); - Hash hash = null; if (details != null) { - hash = details.getHash(); message = details.getSubject(); refs = (List)myDataPack.getRefsModel().refsToCommit(details.getHash()); } - return new GraphCommitCell(graphPrintCell, message, refs); + return new GraphCommitCell(message, refs); } @NotNull @@ -120,19 +87,24 @@ public class GraphTableModel extends AbstractVcsLogTableModel() { + @Override + public boolean value(Integer integer) { + return integer == commitIndex; + } + }); } @Override - public int getRowOfCommitByPartOfHash(@NotNull String hash) { - Node node = myDataPack.getNodeByPartOfHash(hash); - return node != null ? node.getRowIndex() : -1; + public int getRowOfCommitByPartOfHash(@NotNull String partialHash) { + Hash hash = myDataHolder.findHashByString(partialHash); + return hash != null ? getRowOfCommit(hash) : -1; } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/NoGraphTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/NoGraphTableModel.java index 1cf9474b4a58..e8e68e3b3c33 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/NoGraphTableModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/NoGraphTableModel.java @@ -2,97 +2,49 @@ package com.intellij.vcs.log.ui.tables; import com.intellij.openapi.diagnostic.Attachment; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcs.log.Hash; -import com.intellij.vcs.log.VcsFullCommitDetails; import com.intellij.vcs.log.VcsRef; import com.intellij.vcs.log.VcsShortCommitDetails; +import com.intellij.vcs.log.data.DataPack; import com.intellij.vcs.log.data.LoadMoreStage; -import com.intellij.vcs.log.data.RefsModel; +import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.graph.render.CommitCell; import com.intellij.vcs.log.ui.VcsLogUI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -public class NoGraphTableModel extends AbstractVcsLogTableModel { +public class NoGraphTableModel extends AbstractVcsLogTableModel { private static final Logger LOG = Logger.getInstance(NoGraphTableModel.class); - @NotNull private final VcsLogUI myUi; - @NotNull private final List myCommits; - @NotNull private final RefsModel myRefsModel; - @NotNull private final LoadMoreStage myLoadMoreStage; - @NotNull private final AtomicBoolean myLoadMoreWasRequested = new AtomicBoolean(); + @NotNull private final List> myCommitsWithRoots; - public NoGraphTableModel(@NotNull VcsLogUI UI, @NotNull List commits, @NotNull RefsModel refsModel, - @NotNull LoadMoreStage loadMoreStage) { - myUi = UI; - myCommits = commits; - myRefsModel = refsModel; - myLoadMoreStage = loadMoreStage; + public NoGraphTableModel(@NotNull DataPack dataPack, @NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUI ui, + @NotNull List> commitsWithRoots, @NotNull LoadMoreStage loadMoreStage) { + super(logDataHolder, ui, dataPack, loadMoreStage); + myCommitsWithRoots = commitsWithRoots; } @Override public int getRowCount() { - return myCommits.size(); - } - - @Nullable - @Override - protected VcsShortCommitDetails getShortDetails(int rowIndex) { - return getFullCommitDetails(rowIndex); - } - - @Nullable - @Override - public VcsFullCommitDetails getFullCommitDetails(int rowIndex) { - VcsFullCommitDetails commits = myCommits.get(rowIndex); - if (commits == null) { - LOG.error("Couldn't identify details for commit at " + rowIndex, new Attachment("loaded_commits", myCommits.toString())); - } - return commits; - } - - @Override - public void requestToLoadMore(@NotNull Runnable onLoaded) { - if (myLoadMoreWasRequested.compareAndSet(false, true) // Don't send the request to VCS twice - && myLoadMoreStage != LoadMoreStage.ALL_REQUESTED) { // or when everything possible is loaded - myUi.getTable().setPaintBusy(true); - myUi.getFilterer().requestVcs(myUi.collectFilters(), myLoadMoreStage, onLoaded); - } - } - - @Override - public boolean canRequestMore() { - return myLoadMoreStage != LoadMoreStage.ALL_REQUESTED; - } - - @Nullable - @Override - public List getSelectedChanges(@NotNull List selectedRows) { - List changes = new ArrayList(); - for (int selectedRow : selectedRows) { - changes.addAll(myCommits.get(selectedRow).getChanges()); - } - return changes; + return myCommitsWithRoots.size(); } @NotNull @Override public VirtualFile getRoot(int rowIndex) { - VcsFullCommitDetails commit = myCommits.get(rowIndex); + Pair commit = myCommitsWithRoots.get(rowIndex); if (commit != null) { - return commit.getRoot(); + return commit.getSecond(); } else { - LOG.error("Couldn't identify root for commit at " + rowIndex, new Attachment("loaded_commits", myCommits.toString())); + LOG.error("Couldn't identify root for commit at " + rowIndex, new Attachment("loaded_commits", myCommitsWithRoots.toString())); return FAKE_ROOT; } } @@ -104,7 +56,7 @@ public class NoGraphTableModel extends AbstractVcsLogTableModel refs = Collections.emptyList(); if (details != null) { subject = details.getSubject(); - refs = myRefsModel.refsToCommit(details.getHash()); + refs = myDataPack.getRefsModel().refsToCommit(details.getHash()); } return new CommitCell(subject, refs); } @@ -118,14 +70,13 @@ public class NoGraphTableModel extends AbstractVcsLogTableModel commits) { + StringBuilder s = new StringBuilder(); + for (TimedVcsCommit commit : commits) { + if (s.length() != 0) { + s.append(", "); + } + s.append(commit.getHash().asString()); + } + return s.toString(); + } + + private static void doTest(List started, List expected) { + List sorted = new VcsLogSorter().sortByDateTopoOrder(started); + assertEquals(toStr(expected), toStr(sorted)); + } + + @Test + public void simpleTest() { + doTest(log("1|-a0|-", "3|-a1|-a0", "6|-a2|-a0"), + log("6|-a2|-a0", "3|-a1|-a0", "1|-a0|-")); + } + + @Test + public void severalHeads() { + doTest(log( + "1|-a4|-", + "2|-a3|-a4", + "6|-b1|-b2", + "4|-b2|-a3", + "3|-a2|-a3", + "5|-a1|-a2" + ), + + log("6|-b1|-b2", + "5|-a1|-a2", + "4|-b2|-a3", + "3|-a2|-a3", + "2|-a3|-a4", + "1|-a4|-")); + } + + @Test + public void withMerge() { + doTest(log( + "6|-b1|-b2", + "2|-a3|-a4", + "3|-a2|-a3", + "4|-b2|-a3", + "5|-a1|-a2", + "1|-a0|-b1 a1", + "1|-a4|-" + ), + + log("1|-a0|-b1 a1", + "6|-b1|-b2", + "5|-a1|-a2", + "4|-b2|-a3", + "3|-a2|-a3", + "2|-a3|-a4", + "1|-a4|-")); + } + + @Test + public void severalBranches() { + doTest(log( + "1|-a1|-", + "3|-a3|-a1", + "2|-a2|-", + "4|-a4|-a2" + ), + log("4|-a4|-a2", + "3|-a3|-a1", + "2|-a2|-", + "1|-a1|-" + )); + } + +} diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/XDebuggerUtil.java b/platform/xdebugger-api/src/com/intellij/xdebugger/XDebuggerUtil.java index e33bcaf75b10..8bfae66e04be 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/XDebuggerUtil.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/XDebuggerUtil.java @@ -47,7 +47,7 @@ public abstract class XDebuggerUtil { toggleLineBreakpoint(project, file, line, false); } - public abstract void toggleLineBreakpoint(@NotNull Project project, + public abstract XLineBreakpoint toggleLineBreakpoint(@NotNull Project project, @NotNull VirtualFile file, int line, boolean temporary); @@ -59,7 +59,7 @@ public abstract class XDebuggerUtil { toggleLineBreakpoint(project, type, file, line, false); } - public abstract

    void toggleLineBreakpoint(@NotNull Project project, + public abstract

    XLineBreakpoint toggleLineBreakpoint(@NotNull Project project, @NotNull XLineBreakpointType

    type, @NotNull VirtualFile file, int line, diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerUtilImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerUtilImpl.java index 191b8f2b1cfb..ef1e8c519c69 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerUtilImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerUtilImpl.java @@ -25,6 +25,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; @@ -74,7 +75,7 @@ public class XDebuggerUtilImpl extends XDebuggerUtil { } @Override - public void toggleLineBreakpoint(@NotNull final Project project, @NotNull final VirtualFile file, final int line, boolean temporary) { + public XLineBreakpoint toggleLineBreakpoint(@NotNull final Project project, @NotNull final VirtualFile file, final int line, boolean temporary) { XLineBreakpointType typeWinner = null; for (XLineBreakpointType type : getLineBreakpointTypes()) { if (type.canPutAt(file, line, project) && (typeWinner == null || type.getPriority() > typeWinner.getPriority())) { @@ -82,8 +83,9 @@ public class XDebuggerUtilImpl extends XDebuggerUtil { } } if (typeWinner != null) { - toggleLineBreakpoint(project, typeWinner, file, line, temporary); + return toggleLineBreakpoint(project, typeWinner, file, line, temporary); } + return null; } @Override @@ -97,11 +99,12 @@ public class XDebuggerUtilImpl extends XDebuggerUtil { } @Override - public

    void toggleLineBreakpoint(@NotNull final Project project, + public

    XLineBreakpoint toggleLineBreakpoint(@NotNull final Project project, @NotNull final XLineBreakpointType

    type, @NotNull final VirtualFile file, final int line, final boolean temporary) { + final Ref res = new Ref(); new WriteAction() { @Override protected void run(@NotNull final Result result) { @@ -112,10 +115,11 @@ public class XDebuggerUtilImpl extends XDebuggerUtil { } else { P properties = type.createBreakpointProperties(file, line); - breakpointManager.addLineBreakpoint(type, file.getUrl(), line, properties, temporary); + res.set(breakpointManager.addLineBreakpoint(type, file.getUrl(), line, properties, temporary)); } } }.execute(); + return res.get(); } @Override diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XToggleLineBreakpointActionHandler.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XToggleLineBreakpointActionHandler.java index e100741566ac..f95110db3272 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XToggleLineBreakpointActionHandler.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XToggleLineBreakpointActionHandler.java @@ -81,7 +81,9 @@ public class XToggleLineBreakpointActionHandler extends DebuggerActionHandler { XLineBreakpointType typeWinner = null; int lineWinner = -1; for (int line = lineStart; line <= linesEnd; line++) { + int maxPriority = 0; for (XLineBreakpointType type : lineTypes) { + maxPriority = Math.max(maxPriority, type.getPriority()); final XLineBreakpoint breakpoint = breakpointManager.findBreakpointAtLine(type, file, line); if (breakpoint != null && myTemporary && !breakpoint.isTemporary()) { breakpoint.setTemporary(true); @@ -92,6 +94,10 @@ public class XToggleLineBreakpointActionHandler extends DebuggerActionHandler { } } } + // already found max priority type - stop + if (typeWinner != null && typeWinner.getPriority() == maxPriority) { + break; + } } if (typeWinner != null) { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointManager.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointManager.java index 3aab9e884b2d..6c49ba8b5feb 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointManager.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointManager.java @@ -26,6 +26,7 @@ import com.intellij.openapi.editor.colors.EditorColorsAdapter; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.event.*; +import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.markup.MarkupEditorFilterFactory; import com.intellij.openapi.editor.markup.RangeHighlighter; @@ -49,7 +50,10 @@ import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.XDebuggerUtil; +import com.intellij.xdebugger.breakpoints.SuspendPolicy; import com.intellij.xdebugger.breakpoints.XBreakpoint; +import com.intellij.xdebugger.breakpoints.XLineBreakpoint; +import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; @@ -271,7 +275,20 @@ public class XLineBreakpointManager { @Override public void run() { if (!myProject.isDisposed() && myProject.isInitialized() && file.isValid()) { - XDebuggerUtil.getInstance().toggleLineBreakpoint(myProject, file, line, mouseEvent.isAltDown()); + XLineBreakpoint breakpoint = + XDebuggerUtil.getInstance().toggleLineBreakpoint(myProject, file, line, mouseEvent.isAltDown()); + if (!mouseEvent.isAltDown() && mouseEvent.isShiftDown() && breakpoint != null) { + breakpoint.setSuspendPolicy(SuspendPolicy.NONE); + String selection = editor.getSelectionModel().getSelectedText(); + if (selection != null) { + breakpoint.setLogExpression(selection); + } + else { + breakpoint.setLogMessage(true); + } + // edit breakpoint + DebuggerUIUtil.showXBreakpointEditorBalloon(myProject, mouseEvent.getPoint(), ((EditorEx)editor).getGutterComponentEx(), false, breakpoint); + } } } }); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/grouping/XBreakpointTypeGroup.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/grouping/XBreakpointTypeGroup.java index c0005393a8a4..f1dd95cc47b0 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/grouping/XBreakpointTypeGroup.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/grouping/XBreakpointTypeGroup.java @@ -50,6 +50,9 @@ public class XBreakpointTypeGroup extends XBreakpointGroup { @Override public int compareTo(XBreakpointGroup o) { + if (getName().equals(o.getName())) { + return 0; + } if (o instanceof XBreakpointTypeGroup) { if (((XBreakpointTypeGroup)o).myBreakpointType instanceof XLineBreakpointType) { if (myBreakpointType instanceof XLineBreakpointType) { diff --git a/plugins/IntelliLang/src/META-INF/intellilang-java-support.xml b/plugins/IntelliLang/src/META-INF/intellilang-java-support.xml index cef677728345..f5d9305e6d9b 100644 --- a/plugins/IntelliLang/src/META-INF/intellilang-java-support.xml +++ b/plugins/IntelliLang/src/META-INF/intellilang-java-support.xml @@ -28,6 +28,10 @@ + + + + diff --git a/plugins/IntelliLang/src/tips/InjectSQLLanguage.html b/plugins/IntelliLang/src/tips/InjectSQLLanguage.html new file mode 100644 index 000000000000..3d2adac1adab --- /dev/null +++ b/plugins/IntelliLang/src/tips/InjectSQLLanguage.html @@ -0,0 +1,19 @@ + + + + + +

    + Use intention Inject Language &shortcut:ShowIntentionActions; on String literal in Java + class to get code assistance for SQL queries. +

    + +

    +

    + +

    IntelliJ IDEA will provide syntax highlighting, smart completion, navigation and on-the-fly code analysis for your queries.

    + +

    +

    + + \ No newline at end of file diff --git a/plugins/IntelliLang/src/tips/images/assistance_in_sql.png b/plugins/IntelliLang/src/tips/images/assistance_in_sql.png new file mode 100644 index 000000000000..ba432957b5ed Binary files /dev/null and b/plugins/IntelliLang/src/tips/images/assistance_in_sql.png differ diff --git a/plugins/IntelliLang/src/tips/images/inject_language.png b/plugins/IntelliLang/src/tips/images/inject_language.png new file mode 100644 index 000000000000..56d5059e66b9 Binary files /dev/null and b/plugins/IntelliLang/src/tips/images/inject_language.png differ diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/SplitMultiCatchIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/SplitMultiCatchIntention.java index c58edc18b433..d6de66af803a 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/SplitMultiCatchIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/SplitMultiCatchIntention.java @@ -16,15 +16,12 @@ package com.siyeh.ipp.exceptions; import com.intellij.psi.*; -import com.intellij.psi.impl.PsiImplUtil; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.util.IncorrectOperationException; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; import org.jetbrains.annotations.NotNull; -import java.util.List; - import static com.intellij.util.ObjectUtils.assertNotNull; public class SplitMultiCatchIntention extends Intention { @@ -56,27 +53,16 @@ public class SplitMultiCatchIntention extends Intention { } final PsiElementFactory factory = JavaPsiFacade.getElementFactory(element.getProject()); - final List disjunctions = PsiTreeUtil.getChildrenOfTypeAsList(parameter.getTypeElement(), PsiTypeElement.class); - for (int i = 0; i < disjunctions.size(); i++) { + final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(element.getProject()); + for (PsiType disjunction : ((PsiDisjunctionType)type).getDisjunctions()) { final PsiCatchSection copy = (PsiCatchSection)catchSection.copy(); final PsiTypeElement typeElement = assertNotNull(assertNotNull(copy.getParameter()).getTypeElement()); - final PsiTypeElement newTypeElement = factory.createTypeElementFromText(disjunctions.get(i).getText(), catchSection); - typeElement.replace(newTypeElement); + final PsiTypeElement newTypeElement = factory.createTypeElementFromText(disjunction.getCanonicalText(true), catchSection); + final PsiElement replaced = typeElement.replace(newTypeElement); grandParent.addBefore(copy, catchSection); - - if (i == 0) { - // clear the original from type annotations: they belong to the first disjunction and should not appear in others - final PsiModifierList modifierList = parameter.getModifierList(); - if (modifierList != null) { - for (PsiAnnotation annotation : modifierList.getAnnotations()) { - if (PsiImplUtil.findApplicableTarget(annotation, PsiAnnotation.TargetType.TYPE_USE) == PsiAnnotation.TargetType.TYPE_USE) { - annotation.delete(); - } - } - } - } + styleManager.shortenClassReferences(replaced); } catchSection.delete(); diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/splitMultiCatch/TypeAnno.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/splitMultiCatch/TypeAnno.java index 9768360bedb6..51b86343d0a8 100644 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/splitMultiCatch/TypeAnno.java +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/splitMultiCatch/TypeAnno.java @@ -1,5 +1,18 @@ -package com.siyeh.ipp.exceptions.splitMultiCatch; - +/* + * 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. + */ import java.io.*; import java.lang.annotation.*; diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/splitMultiCatch/TypeAnno_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/splitMultiCatch/TypeAnno_after.java index d8d23ae53a2a..019809da9690 100644 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/splitMultiCatch/TypeAnno_after.java +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/exceptions/splitMultiCatch/TypeAnno_after.java @@ -1,5 +1,18 @@ -package com.siyeh.ipp.exceptions.splitMultiCatch; - +/* + * 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. + */ import java.io.*; import java.lang.annotation.*; diff --git a/plugins/git4idea/remote-servers-git/remote-servers-git.iml b/plugins/git4idea/remote-servers-git/remote-servers-git.iml index b9f613800a04..154f59125497 100644 --- a/plugins/git4idea/remote-servers-git/remote-servers-git.iml +++ b/plugins/git4idea/remote-servers-git/remote-servers-git.iml @@ -15,6 +15,8 @@ + + diff --git a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStep.form b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStep.form new file mode 100644 index 000000000000..08bd5c10fa89 --- /dev/null +++ b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStep.form @@ -0,0 +1,34 @@ + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStep.java b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStep.java new file mode 100644 index 000000000000..1b433931683f --- /dev/null +++ b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStep.java @@ -0,0 +1,127 @@ +/* + * 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.remoteServer.util.importProject; + +import com.intellij.ide.util.importProject.ModuleDescriptor; +import com.intellij.ide.util.importProject.ProjectDescriptor; +import com.intellij.ide.util.projectWizard.ModuleBuilder; +import com.intellij.ide.util.projectWizard.ModuleWizardStep; +import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot; +import com.intellij.ide.util.projectWizard.importSources.DetectedSourceRoot; +import com.intellij.ide.util.projectWizard.importSources.ProjectFromSourcesBuilder; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.StdModuleTypes; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.remoteServer.util.CloudAccountSelectionEditor; +import com.intellij.remoteServer.util.CloudBundle; +import com.intellij.remoteServer.util.CloudDeploymentNameConfiguration; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author michael.golubev + */ +public class CloudGitChooseAccountStep extends ModuleWizardStep { + + private JPanel myAccountSelectionPanelPlaceHolder; + private JPanel myMainPanel; + private JLabel myTitleLabel; + + private final CloudGitProjectStructureDetectorBase myDetector; + private final ProjectFromSourcesBuilder myBuilder; + private final ProjectDescriptor myProjectDescriptor; + + private CloudAccountSelectionEditor myEditor; + + public CloudGitChooseAccountStep(CloudGitProjectStructureDetectorBase detector, + ProjectFromSourcesBuilder builder, + ProjectDescriptor projectDescriptor) { + myDetector = detector; + myBuilder = builder; + myProjectDescriptor = projectDescriptor; + + myTitleLabel.setText(CloudBundle.getText("choose.account.title", detector.getDetectorId())); + + myEditor = detector.createAccountSelectionEditor(); + myAccountSelectionPanelPlaceHolder.add(myEditor.getMainPanel()); + myEditor.initUI(); + } + + @Override + public JComponent getComponent() { + return myMainPanel; + } + + @Override + public void updateDataModel() { + Collection roots = myBuilder.getProjectRoots(myDetector); + + CloudGitProjectRoot lastProjectRoot = null; + MultiMap project2sourceRoots = new MultiMap(); + + for (DetectedProjectRoot root : roots) { + if (root instanceof CloudGitProjectRoot) { + lastProjectRoot = (CloudGitProjectRoot)root; + project2sourceRoots.put(lastProjectRoot, new ArrayList()); + } + else if (root instanceof DetectedSourceRoot) { + project2sourceRoots.putValue(lastProjectRoot, (DetectedSourceRoot)root); + } + } + + List modules = new ArrayList(); + for (Map.Entry> project2sourceRootsEntry : project2sourceRoots.entrySet()) { + CloudGitProjectRoot projectRoot = project2sourceRootsEntry.getKey(); + ModuleDescriptor moduleDescriptor + = new ModuleDescriptor(projectRoot.getDirectory(), StdModuleTypes.JAVA, project2sourceRootsEntry.getValue()); + final String applicationName = projectRoot.getApplicationName(); + moduleDescriptor.addConfigurationUpdater(new ModuleBuilder.ModuleConfigurationUpdater() { + + @Override + public void update(@NotNull Module module, @NotNull ModifiableRootModel rootModel) { + createRunConfiguration(module, applicationName); + } + }); + modules.add(moduleDescriptor); + } + myProjectDescriptor.setModules(modules); + } + + @Override + public boolean validate() throws ConfigurationException { + myEditor.validate(); + return super.validate(); + } + + private void createRunConfiguration(Module module, String applicationName) { + DC deploymentConfiguration = myDetector.createDeploymentConfiguration(); + + boolean defaultName = module.getName().equals(applicationName); + deploymentConfiguration.setDefaultDeploymentName(defaultName); + if (!defaultName) { + deploymentConfiguration.setDeploymentName(applicationName); + } + + myEditor.createRunConfiguration(module, deploymentConfiguration); + } +} diff --git a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitJavaSourceRoot.java b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitJavaSourceRoot.java new file mode 100644 index 000000000000..0d1267128775 --- /dev/null +++ b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitJavaSourceRoot.java @@ -0,0 +1,39 @@ +/* + * 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.remoteServer.util.importProject; + +import com.intellij.ide.util.projectWizard.importSources.DetectedSourceRoot; +import com.intellij.ide.util.projectWizard.importSources.JavaModuleSourceRoot; +import org.jetbrains.annotations.NotNull; + +/** + * @author michael.golubev + */ +public class CloudGitJavaSourceRoot extends DetectedSourceRoot { + + private final String myRootTypeName; + + public CloudGitJavaSourceRoot(String rootTypeName, JavaModuleSourceRoot javaSourceRoot) { + super(javaSourceRoot.getDirectory(), javaSourceRoot.getPackagePrefix()); + myRootTypeName = rootTypeName; + } + + @NotNull + @Override + public String getRootTypeName() { + return myRootTypeName; + } +} diff --git a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitProjectRoot.java b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitProjectRoot.java new file mode 100644 index 000000000000..b78f8b9ab4ff --- /dev/null +++ b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitProjectRoot.java @@ -0,0 +1,53 @@ +/* + * 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.remoteServer.util.importProject; + +import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +/** + * @author michael.golubev + */ +public class CloudGitProjectRoot extends DetectedProjectRoot { + + private final String myRootTypeName; + private final String myJavaSourceRootTypeName; + private final String myApplicationName; + + public CloudGitProjectRoot(String rootTypeName, String javaSourceRootTypeName, @NotNull File directory, String applicationName) { + super(directory); + myRootTypeName = rootTypeName; + myJavaSourceRootTypeName = javaSourceRootTypeName; + myApplicationName = applicationName; + } + + @NotNull + @Override + public String getRootTypeName() { + return myRootTypeName; + } + + @Override + public boolean canContainRoot(@NotNull DetectedProjectRoot root) { + return myJavaSourceRootTypeName.equals(root.getRootTypeName()); + } + + public String getApplicationName() { + return myApplicationName; + } +} diff --git a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitProjectStructureDetectorBase.java b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitProjectStructureDetectorBase.java new file mode 100644 index 000000000000..00be96eb5ae4 --- /dev/null +++ b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitProjectStructureDetectorBase.java @@ -0,0 +1,134 @@ +/* + * 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.remoteServer.util.importProject; + +import com.intellij.ide.util.importProject.ProjectDescriptor; +import com.intellij.ide.util.projectWizard.ModuleWizardStep; +import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot; +import com.intellij.ide.util.projectWizard.importSources.JavaModuleSourceRoot; +import com.intellij.ide.util.projectWizard.importSources.ProjectFromSourcesBuilder; +import com.intellij.ide.util.projectWizard.importSources.ProjectStructureDetector; +import com.intellij.ide.util.projectWizard.importSources.impl.JavaProjectStructureDetector; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.remoteServer.ServerType; +import com.intellij.remoteServer.util.CloudAccountSelectionEditor; +import com.intellij.remoteServer.util.CloudDeploymentNameConfiguration; +import com.intellij.remoteServer.util.CloudGitDeploymentDetector; +import git4idea.GitPlatformFacade; +import git4idea.GitUtil; +import git4idea.repo.GitRepository; +import git4idea.repo.GitRepositoryImpl; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author michael.golubev + */ +public abstract class CloudGitProjectStructureDetectorBase extends ProjectStructureDetector { + + private final JavaProjectStructureDetector myJavaDetector = new JavaProjectStructureDetector(); + + private final String myId; + private final String myJavaSourceRootTypeName; + private final CloudGitDeploymentDetector myDeploymentDetector; + + protected CloudGitProjectStructureDetectorBase(ServerType serverType, CloudGitDeploymentDetector deploymentDetector) { + myId = serverType.getPresentableName(); + myJavaSourceRootTypeName = "Java/" + myId; + myDeploymentDetector = deploymentDetector; + } + + @NotNull + @Override + public DirectoryProcessingResult detectRoots(@NotNull File dir, + @NotNull File[] children, + @NotNull File base, + @NotNull List result) { + detectApplicationRoot(dir, result); + + for (DetectedProjectRoot projectRoot : result) { + if ((projectRoot instanceof CloudGitProjectRoot) && FileUtil.isAncestor(projectRoot.getDirectory(), dir, true)) { + return detectJavaRoots(dir, children, base, result); + } + } + + return DirectoryProcessingResult.PROCESS_CHILDREN; + } + + private void detectApplicationRoot(@NotNull File dir, @NotNull List result) { + VirtualFile contentRoot = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(dir); + if (contentRoot == null) { + return; + } + + final File gitDir = new File(dir, GitUtil.DOT_GIT); + if (!gitDir.exists()) { + return; + } + + Project project = ProjectManager.getInstance().getDefaultProject(); + GitRepository repository + = GitRepositoryImpl.getLightInstance(contentRoot, project, ServiceManager.getService(project, GitPlatformFacade.class), project); + repository.update(); + + List applicationNames = myDeploymentDetector.collectApplicationNames(repository); + if (applicationNames.isEmpty()) { + return; + } + + result.add(new CloudGitProjectRoot(myId, myJavaSourceRootTypeName, dir, applicationNames.get(0))); + } + + private DirectoryProcessingResult detectJavaRoots(@NotNull File dir, + @NotNull File[] children, + @NotNull File base, + @NotNull List result) { + List detectedJavaRoots = new ArrayList(); + DirectoryProcessingResult processingResult = myJavaDetector.detectRoots(dir, children, base, detectedJavaRoots); + for (DetectedProjectRoot detectedJavaRoot : detectedJavaRoots) { + if (detectedJavaRoot instanceof JavaModuleSourceRoot) { + result.add(new CloudGitJavaSourceRoot(myJavaSourceRootTypeName, (JavaModuleSourceRoot)detectedJavaRoot)); + } + } + return processingResult; + } + + @Override + public String getDetectorId() { + return myId; + } + + @Override + public List createWizardSteps(ProjectFromSourcesBuilder builder, + ProjectDescriptor projectDescriptor, + Icon stepIcon) { + return Collections.singletonList(new CloudGitChooseAccountStep(this, builder, projectDescriptor)); + } + + public abstract CloudAccountSelectionEditor createAccountSelectionEditor(); + + public abstract DC createDeploymentConfiguration(); +} diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index 8664c8ae51e2..3e36a5824540 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -168,8 +168,6 @@ - parentRevisions) throws VcsException { - final GitContentRevision contentRevision = createRevisionImpl(file, currentRevision, project, null); - if (parentRevisions.size() == 1) { - return contentRevision; - } else { - if (contentRevision instanceof GitBinaryContentRevision) { - return new GitBinaryMultipleContentsRevision(file, parentRevisions, (GitBinaryContentRevision) contentRevision); - } else { - return new GitMultipleContentsRevision(file, parentRevisions, contentRevision); - } - } - } - /** * Create revision * diff --git a/plugins/git4idea/src/git4idea/GitPlatformFacade.java b/plugins/git4idea/src/git4idea/GitPlatformFacade.java index d808c1b69724..450936045fd7 100644 --- a/plugins/git4idea/src/git4idea/GitPlatformFacade.java +++ b/plugins/git4idea/src/git4idea/GitPlatformFacade.java @@ -22,9 +22,6 @@ import git4idea.config.GitVcsSettings; import git4idea.repo.GitRepository; import org.jetbrains.annotations.NotNull; -/** - * @author Kirill Likhodedov - */ public interface GitPlatformFacade extends DvcsPlatformFacade { @NotNull @@ -32,8 +29,4 @@ public interface GitPlatformFacade extends DvcsPlatformFacade { @NotNull GitVcsSettings getSettings(Project project); - - @NotNull - Notificator getNotificator(@NotNull Project project); - } diff --git a/plugins/git4idea/src/git4idea/GitPlatformFacadeImpl.java b/plugins/git4idea/src/git4idea/GitPlatformFacadeImpl.java index 09ed8f519e07..24b0d5387b72 100644 --- a/plugins/git4idea/src/git4idea/GitPlatformFacadeImpl.java +++ b/plugins/git4idea/src/git4idea/GitPlatformFacadeImpl.java @@ -24,9 +24,6 @@ import git4idea.config.GitVcsSettings; import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; -/** - * @author Kirill Likhodedov - */ class GitPlatformFacadeImpl extends DvcsPlatformFacadeImpl implements GitPlatformFacade { @NotNull @@ -35,12 +32,6 @@ class GitPlatformFacadeImpl extends DvcsPlatformFacadeImpl implements GitPlatfor return ProjectLevelVcsManager.getInstance(project).findVcsByName(GitVcs.NAME); } - @NotNull - @Override - public Notificator getNotificator(@NotNull Project project) { - return Notificator.getInstance(project); - } - @NotNull @Override public GitRepositoryManager getRepositoryManager(@NotNull Project project) { diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index 714f6096a3e4..ae6ef8e4a037 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -18,7 +18,8 @@ package git4idea; import com.intellij.dvcs.DvcsUtil; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.ide.BrowserUtil; -import com.intellij.notification.*; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationListener; import com.intellij.notification.impl.NotificationsConfigurationImpl; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.application.ApplicationManager; @@ -38,7 +39,6 @@ import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangeProvider; import com.intellij.openapi.vcs.changes.CommitExecutor; -import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager; import com.intellij.openapi.vcs.checkin.CheckinEnvironment; import com.intellij.openapi.vcs.diff.DiffProvider; import com.intellij.openapi.vcs.diff.RevisionSelector; @@ -99,12 +99,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * Git VCS implementation */ public class GitVcs extends AbstractVcs { - public static final NotificationGroup NOTIFICATION_GROUP_ID = NotificationGroup.toolWindowGroup( - "Git Messages", ChangesViewContentManager.TOOLWINDOW_ID, true); - public static final NotificationGroup IMPORTANT_ERROR_NOTIFICATION = new NotificationGroup( - "Git Important Messages", NotificationDisplayType.STICKY_BALLOON, true); - public static final NotificationGroup MINOR_NOTIFICATION = new NotificationGroup( - "Git Minor Notifications", NotificationDisplayType.BALLOON, true); static { NotificationsConfigurationImpl.remove("Git"); @@ -455,18 +449,21 @@ public class GitVcs extends AbstractVcs { String message = String.format("The configured version of Git is not supported: %s.
    " + "The minimal supported version is %s. Please update.", myVersion, GitVersion.MIN); - IMPORTANT_ERROR_NOTIFICATION.createNotification("Unsupported Git version", message, NotificationType.ERROR, - new NotificationListener.Adapter() { - @Override - protected void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent e) { - if (SETTINGS_LINK.equals(e.getDescription())) { - ShowSettingsUtil.getInstance().showSettingsDialog(myProject, getConfigurable().getDisplayName()); - } - else if (UPDATE_LINK.equals(e.getDescription())) { - BrowserUtil.browse("http://git-scm.com"); - } - } - }).notify(myProject); + VcsNotifier.getInstance(myProject).notifyError("Unsupported Git version", message, + new NotificationListener.Adapter() { + @Override + protected void hyperlinkActivated(@NotNull Notification notification, + @NotNull HyperlinkEvent e) { + if (SETTINGS_LINK.equals(e.getDescription())) { + ShowSettingsUtil.getInstance() + .showSettingsDialog(myProject, getConfigurable().getDisplayName()); + } + else if (UPDATE_LINK.equals(e.getDescription())) { + BrowserUtil.browse("http://git-scm.com"); + } + } + } + ); } } catch (Exception e) { if (getExecutableValidator().checkExecutableAndNotifyIfNeeded()) { // check executable before notifying error @@ -587,7 +584,7 @@ public class GitVcs extends AbstractVcs { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { public void run() { Collection roots = ServiceManager.getService(myProject, VcsRootDetector.class).detect(); - new GitIntegrationEnabler(myProject, myGit, myPlatformFacade).enable(roots); + new GitIntegrationEnabler(GitVcs.this, myGit).enable(roots); } }); } diff --git a/plugins/git4idea/src/git4idea/Notificator.java b/plugins/git4idea/src/git4idea/Notificator.java deleted file mode 100644 index 9943af7cf3e4..000000000000 --- a/plugins/git4idea/src/git4idea/Notificator.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package git4idea; - -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationGroup; -import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author Kirill Likhodedov - */ -public class Notificator { - - private final @NotNull Project myProject; - - public static Notificator getInstance(@NotNull Project project) { - return ServiceManager.getService(project, Notificator.class); - } - - public Notificator(@NotNull Project project) { - myProject = project; - } - - @NotNull - public static Notification createNotification(@NotNull NotificationGroup notificationGroup, - @NotNull String title, @NotNull String message, @NotNull NotificationType type, - @Nullable NotificationListener listener) { - // title can be empty; description can't be neither null, nor empty - if (StringUtil.isEmptyOrSpaces(message)) { - message = title; - title = ""; - } - // if both title and description were empty, then it is a problem in the calling code => Notifications engine assertion will notify. - return notificationGroup.createNotification(title, message, type, listener); - } - - public void notify(@NotNull Notification notification) { - notification.notify(myProject); - } - - public void notify(@NotNull NotificationGroup notificationGroup, @NotNull String title, @NotNull String message, - @NotNull NotificationType type, @Nullable NotificationListener listener) { - createNotification(notificationGroup, title, message, type, listener).notify(myProject); - } - - public void notify(@NotNull NotificationGroup notificationGroup, @NotNull String title, @NotNull String message, - @NotNull NotificationType type) { - notify(notificationGroup, title, message, type, null); - } - - public void notifyError(@NotNull String title, @NotNull String message) { - notifyError(title, message, null); - } - - public void notifyError(@NotNull String title, @NotNull String message, @Nullable NotificationListener listener) { - notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, title, message, NotificationType.ERROR, listener); - } - - public void notifySuccess(@NotNull String title, @NotNull String message) { - notify(GitVcs.NOTIFICATION_GROUP_ID, title, message, NotificationType.INFORMATION, null); - } - - public void notifyWeakWarning(@NotNull String title, @NotNull String message, @Nullable NotificationListener listener) { - notify(GitVcs.MINOR_NOTIFICATION, title, message, NotificationType.WARNING, listener); - } - - public void notifyStrongWarning(@NotNull String title, @NotNull String content, @Nullable NotificationListener listener) { - notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, title, content, NotificationType.WARNING, listener); - } - -} diff --git a/plugins/git4idea/src/git4idea/actions/GitCompareWithBranchAction.java b/plugins/git4idea/src/git4idea/actions/GitCompareWithBranchAction.java index 231fd8e6d5d1..f0ad3b7a5e31 100644 --- a/plugins/git4idea/src/git4idea/actions/GitCompareWithBranchAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitCompareWithBranchAction.java @@ -15,10 +15,8 @@ */ package git4idea.actions; -import com.intellij.notification.NotificationType; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAwareAction; @@ -214,7 +212,7 @@ public class GitCompareWithBranchAction extends DumbAwareAction { GitUIUtil.notifyMessage(project, GitUtil.fileOrFolder(file) + " doesn't exist in branch", String.format("%s %s doesn't exist in branch %s", GitUtil.fileOrFolder(file), file.getPresentableUrl(), branchToCompare), - NotificationType.WARNING, true, null); + false, null); } } diff --git a/plugins/git4idea/src/git4idea/actions/GitInit.java b/plugins/git4idea/src/git4idea/actions/GitInit.java index aec2fb6d5b51..7d6a7f3945e9 100644 --- a/plugins/git4idea/src/git4idea/actions/GitInit.java +++ b/plugins/git4idea/src/git4idea/actions/GitInit.java @@ -15,7 +15,6 @@ */ package git4idea.actions; -import com.intellij.notification.NotificationType; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.components.ServiceManager; @@ -31,6 +30,7 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsDirectoryMapping; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; import com.intellij.vcsUtil.VcsFileUtil; @@ -39,7 +39,6 @@ import git4idea.GitVcs; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; import git4idea.i18n.GitBundle; -import git4idea.util.GitUIUtil; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -85,8 +84,7 @@ public class GitInit extends DumbAwareAction { if (!result.success()) { GitVcs vcs = GitVcs.getInstance(project); if (vcs != null && vcs.getExecutableValidator().checkExecutableAndNotifyIfNeeded()) { - GitUIUtil.notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, project, "Git init failed", result.getErrorOutputAsHtmlString(), - NotificationType.ERROR, null); + VcsNotifier.getInstance(project).notifyError("Git init failed", result.getErrorOutputAsHtmlString()); } return; } diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java index 9cc47791bca2..b65d4cc6097e 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java @@ -15,7 +15,6 @@ */ package git4idea.branch; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; @@ -23,6 +22,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Function; @@ -41,8 +41,6 @@ import static com.intellij.openapi.util.text.StringUtil.pluralize; /** * Common class for Git operations with branches aware of multi-root configuration, * which means showing combined error information, proposing to rollback, etc. - * - * @author Kirill Likhodedov */ abstract class GitBranchOperation { @@ -155,7 +153,7 @@ abstract class GitBranchOperation { } protected void notifySuccess(@NotNull String message) { - myUiHandler.notifySuccess(message); + VcsNotifier.getInstance(myProject).notifySuccess(message); } protected final void notifySuccess() { @@ -190,12 +188,7 @@ abstract class GitBranchOperation { } protected void notifyError(@NotNull String title, @NotNull String message) { - if (ApplicationManager.getApplication().isUnitTestMode()) { - throw new RuntimeException(title + ": " + message); - } - else { - myUiHandler.notifyError(title, message); - } + VcsNotifier.getInstance(myProject).notifyError(title, message); } @NotNull diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java index aa644146e5b0..9a153bb9dfed 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java @@ -15,7 +15,6 @@ */ package git4idea.branch; -import com.intellij.notification.NotificationListener; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.changes.Change; @@ -23,7 +22,6 @@ import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitCommit; import git4idea.repo.GitRepository; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; @@ -33,25 +31,12 @@ import java.util.Map; *

    Handles UI interaction during various operations on branches: shows notifications, proposes to rollback, shows dialogs, messages, etc. * Some methods return the choice selected by user to the calling code, if it is needed.

    *

    The purpose of this class is to separate UI interaction from the main code, which would in particular simplify testing.

    - * - * @author Kirill Likhodedov */ public interface GitBranchUiHandler { @NotNull ProgressIndicator getProgressIndicator(); - /** - * Shows a notification about successful branch operation. The title is empty. - */ - void notifySuccess(@NotNull String message); - - void notifySuccess(@NotNull String title, @NotNull String message); - - void notifySuccess(@NotNull String title, @NotNull String description, @Nullable NotificationListener listener); - - void notifyError(@NotNull String title, @NotNull String message); - boolean notifyErrorWithRollbackProposal(@NotNull String title, @NotNull String message, @NotNull String rollbackProposal); /** diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java index 0defd883244f..0c1878a5ec82 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java @@ -17,26 +17,27 @@ package git4idea.branch; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ui.SelectFilesDialog; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.components.JBLabel; import com.intellij.util.ui.UIUtil; import com.intellij.xml.util.XmlStringUtil; -import git4idea.*; +import git4idea.GitCommit; +import git4idea.GitPlatformFacade; +import git4idea.GitUtil; +import git4idea.MessageManager; import git4idea.commands.Git; import git4idea.merge.GitConflictResolver; import git4idea.repo.GitRepository; import git4idea.util.UntrackedFilesNotifier; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.HyperlinkEvent; @@ -46,9 +47,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -/** - * @author Kirill Likhodedov - */ public class GitBranchUiHandlerImpl implements GitBranchUiHandler { @NotNull private final Project myProject; @@ -63,26 +61,6 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler { myProgressIndicator = indicator; } - @Override - public void notifySuccess(@NotNull String message) { - notifySuccess("", message); - } - - @Override - public void notifySuccess(@NotNull String title, @NotNull String message) { - notifySuccess(title, message, null); - } - - @Override - public void notifySuccess(@NotNull String title, @NotNull String description, @Nullable NotificationListener listener) { - Notificator.getInstance(myProject).notify(GitVcs.NOTIFICATION_GROUP_ID, title, description, NotificationType.INFORMATION, listener); - } - - @Override - public void notifyError(@NotNull String title, @NotNull String message) { - Notificator.getInstance(myProject).notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, title, message, NotificationType.ERROR); - } - @Override public boolean notifyErrorWithRollbackProposal(@NotNull final String title, @NotNull final String message, @NotNull final String rollbackProposal) { @@ -106,7 +84,7 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler { public void showUnmergedFilesNotification(@NotNull final String operationName, @NotNull final Collection repositories) { String title = unmergedFilesErrorTitle(operationName); String description = unmergedFilesErrorNotificationDescription(operationName); - Notificator.getInstance(myProject).notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, title, description, NotificationType.ERROR, + VcsNotifier.getInstance(myProject).notifyError(title, description, new NotificationListener() { @Override public void hyperlinkUpdate(@NotNull Notification notification, @@ -142,7 +120,7 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler { @Override public void showUntrackedFilesNotification(@NotNull String operationName, @NotNull Collection untrackedFiles) { - UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, ServiceManager.getService(myProject, GitPlatformFacade.class), + UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, untrackedFiles, operationName, null); } diff --git a/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java index 942d4c050941..22e486e6eab0 100644 --- a/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java @@ -17,6 +17,7 @@ package git4idea.branch; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsNotifier; import git4idea.GitPlatformFacade; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; @@ -31,8 +32,6 @@ import static git4idea.util.GitUIUtil.code; /** * Create new branch (starting from the current branch) and check it out. - * - * @author Kirill Likhodedov */ class GitCheckoutNewBranchOperation extends GitBranchOperation { @@ -113,8 +112,9 @@ class GitCheckoutNewBranchOperation extends GitBranchOperation { refresh(repository); } if (checkoutResult.totalSuccess() && deleteResult.totalSuccess()) { - myUiHandler.notifySuccess("Rollback successful", String.format("Checked out %s and deleted %s on %s %s", code(myCurrentBranchOrRev), code(myNewBranchName), - StringUtil.pluralize("root", repositories.size()), successfulRepositoriesJoined())); + VcsNotifier.getInstance(myProject).notifySuccess("Rollback successful", String + .format("Checked out %s and deleted %s on %s %s", code(myCurrentBranchOrRev), code(myNewBranchName), + StringUtil.pluralize("root", repositories.size()), successfulRepositoriesJoined())); } else { StringBuilder message = new StringBuilder(); @@ -126,7 +126,7 @@ class GitCheckoutNewBranchOperation extends GitBranchOperation { message.append("Errors during deleting ").append(code(myNewBranchName)); message.append(deleteResult.getErrorOutputWithReposIndication()); } - myUiHandler.notifyError("Error during rollback", message.toString()); + VcsNotifier.getInstance(myProject).notifyError("Error during rollback", message.toString()); } } diff --git a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java index a38ce8ae9b47..a48582680fa0 100644 --- a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java @@ -15,19 +15,17 @@ */ package git4idea.branch; -import com.intellij.notification.NotificationType; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import git4idea.GitPlatformFacade; -import git4idea.GitVcs; import git4idea.commands.*; import git4idea.repo.GitRepository; import git4idea.util.GitPreservingProcess; -import git4idea.util.GitUIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -184,8 +182,8 @@ class GitCheckoutOperation extends GitBranchOperation { message.append("Errors during deleting ").append(code(myNewBranch)).append(": "); message.append(deleteResult.getErrorOutputWithReposIndication()); } - GitUIUtil.notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, myProject, "Error during rollback", - message.toString(), NotificationType.ERROR, null); + VcsNotifier.getInstance(myProject).notifyError("Error during rollback", + message.toString()); } } diff --git a/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java index 68e17a9d1af1..d3501caf7089 100644 --- a/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java @@ -15,18 +15,16 @@ */ package git4idea.branch; -import com.intellij.notification.NotificationType; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import git4idea.GitCommit; import git4idea.GitPlatformFacade; -import git4idea.GitVcs; import git4idea.commands.*; import git4idea.repo.GitRepository; -import git4idea.util.GitUIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -121,8 +119,8 @@ class GitDeleteBranchOperation extends GitBranchOperation { } if (!result.totalSuccess()) { - GitUIUtil.notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, myProject, "Error during rollback of branch deletion", - result.getErrorOutputWithReposIndication(), NotificationType.ERROR, null); + VcsNotifier.getInstance(myProject).notifyError("Error during rollback of branch deletion", + result.getErrorOutputWithReposIndication()); } } diff --git a/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java index 29f7bd7f5c99..e1906592233d 100644 --- a/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java @@ -15,18 +15,16 @@ */ package git4idea.branch; -import com.intellij.notification.NotificationType; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.MessageDialogBuilder; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.util.ui.UIUtil; import git4idea.GitBranch; import git4idea.GitPlatformFacade; -import git4idea.GitVcs; -import git4idea.Notificator; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; import git4idea.commands.GitCompoundResult; @@ -45,9 +43,6 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -/** - * @author Kirill Likhodedov - */ class GitDeleteRemoteBranchOperation extends GitBranchOperation { private final String myBranchName; @@ -140,8 +135,8 @@ class GitDeleteRemoteBranchOperation extends GitBranchOperation { repository.update(); } if (!result.totalSuccess()) { - Notificator.getInstance(myProject).notifyError("Failed to delete remote branch " + branchName, - result.getErrorOutputWithReposIndication()); + VcsNotifier.getInstance(myProject).notifyError("Failed to delete remote branch " + branchName, + result.getErrorOutputWithReposIndication()); } return result.totalSuccess(); } @@ -210,8 +205,8 @@ class GitDeleteRemoteBranchOperation extends GitBranchOperation { if (!localBranches.isEmpty()) { message = "Also deleted local " + StringUtil.pluralize("branch", localBranches.size()) + ": " + StringUtil.join(localBranches, ", "); } - Notificator.getInstance(myProject).notify(GitVcs.NOTIFICATION_GROUP_ID, "Deleted remote branch " + remoteBranchName, - message, NotificationType.INFORMATION); + VcsNotifier.getInstance(myProject).notifySuccess("Deleted remote branch " + remoteBranchName, + message); } private DeleteRemoteBranchDecision confirmBranchDeletion(@NotNull String branchName, @NotNull Collection trackingBranches, diff --git a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java index 43c325fe0886..81e5cc35f750 100644 --- a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java @@ -18,20 +18,18 @@ package git4idea.branch; import com.intellij.dvcs.DvcsUtil; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ui.UIUtil; import git4idea.GitPlatformFacade; import git4idea.GitUtil; -import git4idea.GitVcs; -import git4idea.Notificator; import git4idea.commands.*; import git4idea.merge.GitMergeCommittingConflictResolver; import git4idea.merge.GitMerger; @@ -43,9 +41,6 @@ import javax.swing.event.HyperlinkEvent; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; -/** - * @author Kirill Likhodedov - */ class GitMergeOperation extends GitBranchOperation { private static final Logger LOG = Logger.getInstance(GitMergeOperation.class); @@ -170,7 +165,7 @@ class GitMergeOperation extends GitBranchOperation { break; case PROPOSE: String description = message + "
    Delete " + myBranchToMerge + ""; - myUiHandler.notifySuccess("", description, new DeleteMergedLocalBranchNotificationListener()); + VcsNotifier.getInstance(myProject).notifySuccess("", description, new DeleteMergedLocalBranchNotificationListener()); break; case NOTHING: super.notifySuccess(message); @@ -295,7 +290,7 @@ class GitMergeOperation extends GitBranchOperation { myConflictedRepositories.clear(); if (!result.totalSuccess()) { - Notificator.getInstance(myProject).notifyError("Error during rollback", result.getErrorOutputWithReposIndication()); + VcsNotifier.getInstance(myProject).notifyError("Error during rollback", result.getErrorOutputWithReposIndication()); } LOG.info("rollback finished."); } @@ -369,10 +364,9 @@ class GitMergeOperation extends GitBranchOperation { @Override protected void notifyUnresolvedRemain() { - Notificator.getInstance(myProject).notify( - GitVcs.IMPORTANT_ERROR_NOTIFICATION, "Merged branch " + myBranchToMerge + " with conflicts", - "Unresolved conflicts remain in the project. Resolve now.", NotificationType.WARNING, - getResolveLinkListener()); + VcsNotifier.getInstance(myProject).notifyImportantWarning("Merged branch " + myBranchToMerge + " with conflicts", + "Unresolved conflicts remain in the project. Resolve now.", + getResolveLinkListener()); } } diff --git a/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java b/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java index 9b3e587ef3bb..a8b78a151423 100644 --- a/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java +++ b/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java @@ -20,11 +20,11 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.CheckoutProvider; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitVcs; -import git4idea.Notificator; import git4idea.actions.BasicAction; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; @@ -127,7 +127,7 @@ public class GitCheckoutProvider implements CheckoutProvider { if (result.success()) { return true; } - Notificator.getInstance(project).notifyError("Clone failed", result.getErrorOutputAsHtmlString()); + VcsNotifier.getInstance(project).notifyError("Clone failed", result.getErrorOutputAsHtmlString()); return false; } diff --git a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPicker.java b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPicker.java index 4c288c5701f6..a19ea0726201 100644 --- a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPicker.java +++ b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPicker.java @@ -21,6 +21,7 @@ import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; @@ -136,7 +137,7 @@ public class GitCherryPicker { "Please move, remove or add them before you can cherry-pick. View them"; description += getSuccessfulCommitDetailsIfAny(successfulCommits); - UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, myPlatformFacade, untrackedFilesDetector.getFiles(), + UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, untrackedFilesDetector.getFiles(), "cherry-pick", description); return false; } @@ -182,7 +183,7 @@ public class GitCherryPicker { String description = commitDetails(commit) + "
    Unresolved conflicts remain in the working tree. Resolve them."; description += getSuccessfulCommitDetailsIfAny(successfulCommits); - myPlatformFacade.getNotificator(myProject).notifyStrongWarning("Cherry-picked with conflicts", description, resolveLinkListener); + VcsNotifier.getInstance(myProject).notifyImportantWarning("Cherry-picked with conflicts", description, resolveLinkListener); } private void notifyCommitCancelled(@NotNull GitCommitWrapper commit, @NotNull List successfulCommits) { @@ -192,7 +193,7 @@ public class GitCherryPicker { } String description = commitDetails(commit); description += getSuccessfulCommitDetailsIfAny(successfulCommits); - myPlatformFacade.getNotificator(myProject).notifyWeakWarning("Cherry-pick cancelled", description, null); + VcsNotifier.getInstance(myProject).notifyMinorWarning("Cherry-pick cancelled", description, null); } private CherryPickData updateChangeListManager(@NotNull final VcsFullCommitDetails commit) { @@ -317,10 +318,12 @@ public class GitCherryPicker { } } - private void notifyError(@NotNull String content, @NotNull GitCommitWrapper failedCommit, @NotNull List successfulCommits) { + private void notifyError(@NotNull String content, + @NotNull GitCommitWrapper failedCommit, + @NotNull List successfulCommits) { String description = commitDetails(failedCommit) + "
    " + content; description += getSuccessfulCommitDetailsIfAny(successfulCommits); - myPlatformFacade.getNotificator(myProject).notifyError("Cherry-pick failed", description); + VcsNotifier.getInstance(myProject).notifyError("Cherry-pick failed", description); } @NotNull @@ -335,7 +338,7 @@ public class GitCherryPicker { private void notifySuccess(@NotNull List successfulCommits) { String description = getCommitsDetails(successfulCommits); - myPlatformFacade.getNotificator(myProject).notifySuccess("Cherry-pick successful", description); + VcsNotifier.getInstance(myProject).notifySuccess("Cherry-pick successful", description); } @NotNull diff --git a/plugins/git4idea/src/git4idea/commands/GitTaskResultNotificationHandler.java b/plugins/git4idea/src/git4idea/commands/GitTaskResultNotificationHandler.java index 081a3e31283d..9684f6c92e7d 100644 --- a/plugins/git4idea/src/git4idea/commands/GitTaskResultNotificationHandler.java +++ b/plugins/git4idea/src/git4idea/commands/GitTaskResultNotificationHandler.java @@ -15,13 +15,9 @@ */ package git4idea.commands; -import com.intellij.notification.NotificationType; import com.intellij.openapi.project.Project; -import git4idea.GitVcs; +import com.intellij.openapi.vcs.VcsNotifier; -/** - * @author Kirill Likhodedov - */ public class GitTaskResultNotificationHandler extends GitTaskResultHandlerAdapter { private final Project myProject; private final String mySuccessMessage; @@ -35,15 +31,18 @@ public class GitTaskResultNotificationHandler extends GitTaskResultHandlerAdapte myErrorMessage = errorMessage; } - @Override protected void onSuccess() { - GitVcs.NOTIFICATION_GROUP_ID.createNotification(mySuccessMessage, NotificationType.INFORMATION).notify(myProject); + @Override + protected void onSuccess() { + VcsNotifier.getInstance(myProject).notifySuccess(mySuccessMessage); } - @Override protected void onCancel() { - GitVcs.NOTIFICATION_GROUP_ID.createNotification(myCancelMessage, NotificationType.INFORMATION).notify(myProject); + @Override + protected void onCancel() { + VcsNotifier.getInstance(myProject).notifySuccess(myCancelMessage); } - @Override protected void onFailure() { - GitVcs.IMPORTANT_ERROR_NOTIFICATION.createNotification(myErrorMessage, NotificationType.ERROR).notify(myProject); + @Override + protected void onFailure() { + VcsNotifier.getInstance(myProject).notifyError("", myErrorMessage); } } diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java index f4cc163da2e3..c277df5336d2 100644 --- a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java +++ b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java @@ -42,6 +42,7 @@ import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; import com.intellij.vcs.log.impl.HashImpl; +import com.intellij.vcs.log.util.StopWatch; import git4idea.*; import git4idea.branch.GitBranchUtil; import git4idea.commands.*; @@ -512,9 +513,10 @@ public class GitHistoryUtils { } @NotNull - public static List readAllHashes(@NotNull final Project project, - @NotNull VirtualFile root, - @NotNull final Consumer userRegistry) throws VcsException { + public static List readCommits(@NotNull final Project project, + @NotNull VirtualFile root, + @NotNull final Consumer userRegistry, + @NotNull List parameters) throws VcsException { final int COMMIT_BUFFER = 1000; GitLineHandler h = new GitLineHandler(project, root, GitCommand.LOG); @@ -522,9 +524,9 @@ public class GitHistoryUtils { AUTHOR_NAME, AUTHOR_EMAIL); h.setStdoutSuppressed(true); h.addParameters(parser.getPretty(), "--encoding=UTF-8"); - h.addParameters(LOG_ALL); h.addParameters("--full-history", "--sparse"); h.addParameters("--date-order"); + h.addParameters(parameters); h.endOptions(); final List commits = ContainerUtil.newArrayList(); @@ -740,11 +742,16 @@ public class GitHistoryUtils { h.addParameters("--full-history", "--sparse"); h.endOptions(); + StopWatch sw = StopWatch.start("git log --all-details"); String output = h.run(); + sw.report(); + sw = StopWatch.start("parsing"); List records = parser.parse(output); + sw.report(); - return ContainerUtil.mapNotNull(records, new Function() { + sw = StopWatch.start("Creating GitCommit objects"); + List gitCommits = ContainerUtil.mapNotNull(records, new Function() { @Override public GitCommit fun(GitLogRecord record) { try { @@ -756,6 +763,8 @@ public class GitHistoryUtils { } } }); + sw.report(); + return gitCommits; } private static GitCommit createCommit(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLogRecord record) diff --git a/plugins/git4idea/src/git4idea/history/GitLogRecord.java b/plugins/git4idea/src/git4idea/history/GitLogRecord.java index 6dcaace1aeba..345b347dff41 100644 --- a/plugins/git4idea/src/git4idea/history/GitLogRecord.java +++ b/plugins/git4idea/src/git4idea/history/GitLogRecord.java @@ -219,7 +219,7 @@ class GitLogRecord { } final FilePath filePath = GitContentRevision.createPath(vcsRoot, path, false, true, true); before = GitContentRevision.createRevision(vcsRoot, path, firstParent, project, false, false, true); - after = GitContentRevision.createMultipleParentsRevision(project, filePath, (GitRevisionNumber)thisRevision, parentRevisions); + after = GitContentRevision.createRevision(filePath, thisRevision, project, null); break; case DELETED: status = FileStatus.DELETED; @@ -234,14 +234,13 @@ class GitLogRecord { final FilePath filePathAfterRename = GitContentRevision.createPath(vcsRoot, secondPath == null ? path : secondPath, false, false, true); before = GitContentRevision.createRevision(vcsRoot, path, firstParent, project, true, true, true); - after = GitContentRevision.createMultipleParentsRevision(project, filePathAfterRename, - (GitRevisionNumber)thisRevision, parentRevisions); + after = GitContentRevision.createRevision(filePathAfterRename, thisRevision, project, null); break; case TYPE_CHANGED: status = FileStatus.MODIFIED; final FilePath filePath2 = GitContentRevision.createPath(vcsRoot, path, false, true, true); before = GitContentRevision.createRevision(vcsRoot, path, firstParent, project, false, false, true); - after = GitContentRevision.createMultipleParentsRevision(project, filePath2, (GitRevisionNumber)thisRevision, parentRevisions); + after = GitContentRevision.createRevision(filePath2, thisRevision, project, null); break; default: throw new AssertionError("Unknown file status: " + statusInfo); diff --git a/plugins/git4idea/src/git4idea/history/wholeTree/GitBinaryMultipleContentsRevision.java b/plugins/git4idea/src/git4idea/history/wholeTree/GitBinaryMultipleContentsRevision.java deleted file mode 100644 index 644092498a28..000000000000 --- a/plugins/git4idea/src/git4idea/history/wholeTree/GitBinaryMultipleContentsRevision.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2000-2010 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 git4idea.history.wholeTree; - -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vcs.changes.BinaryContentRevision; -import git4idea.GitBinaryContentRevision; -import git4idea.GitRevisionNumber; - -import java.util.List; - -/** - * @author irengrig - * Date: 12/20/10 - * Time: 3:03 PM - */ -public class GitBinaryMultipleContentsRevision extends GitMultipleContentsRevision implements BinaryContentRevision { - public GitBinaryMultipleContentsRevision(FilePath path, List list, GitBinaryContentRevision contentRevision) - throws VcsException { - super(path, list, contentRevision); - } - - @Override - public byte[] getBinaryContent() throws VcsException { - return ((GitBinaryContentRevision) myRevision).getBinaryContent(); - } -} diff --git a/plugins/git4idea/src/git4idea/history/wholeTree/GitMultipleContentsRevision.java b/plugins/git4idea/src/git4idea/history/wholeTree/GitMultipleContentsRevision.java deleted file mode 100644 index 58b29875f99d..000000000000 --- a/plugins/git4idea/src/git4idea/history/wholeTree/GitMultipleContentsRevision.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2000-2010 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 git4idea.history.wholeTree; - -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vcs.changes.ContentRevision; -import com.intellij.openapi.vcs.history.VcsRevisionNumber; -import git4idea.GitContentRevision; -import git4idea.GitRevisionNumber; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -/** - * @author irengrig - */ -public class GitMultipleContentsRevision implements ContentRevision { - private final FilePath myPath; - private final List myList; - protected ContentRevision myRevision; - - public GitMultipleContentsRevision(final FilePath path, - final List list, - final GitContentRevision contentRevision) throws VcsException { - myPath = path; - myList = list; - myRevision = contentRevision; - } - - @Override - public String getContent() throws VcsException { - return myRevision.getContent(); - } - - @NotNull - @Override - public FilePath getFile() { - return myPath; - } - - @NotNull - @Override - public VcsRevisionNumber getRevisionNumber() { - return myRevision.getRevisionNumber(); - } - - public List getList() { - return myList; - } -} diff --git a/plugins/git4idea/src/git4idea/log/GitLogProvider.java b/plugins/git4idea/src/git4idea/log/GitLogProvider.java index 88aae2a17f3b..e33d846eeafa 100644 --- a/plugins/git4idea/src/git4idea/log/GitLogProvider.java +++ b/plugins/git4idea/src/git4idea/log/GitLogProvider.java @@ -72,8 +72,8 @@ public class GitLogProvider implements VcsLogProvider { return Collections.emptyList(); } - String[] params = ArrayUtil.mergeArrays(ArrayUtil.toStringArray(GitHistoryUtils.LOG_ALL), - "--encoding=UTF-8", "--full-history", "--sparse", "--max-count=" + commitCount); + String[] params = ArrayUtil.mergeArrays(ArrayUtil.toStringArray(GitHistoryUtils.LOG_ALL), "--encoding=UTF-8", "--full-history", + "--sparse", "--max-count=" + commitCount); if (ordered) { params = ArrayUtil.append(params, "--date-order"); } @@ -87,7 +87,7 @@ public class GitLogProvider implements VcsLogProvider { return Collections.emptyList(); } - return GitHistoryUtils.readAllHashes(myProject, root, userRegistry); + return GitHistoryUtils.readCommits(myProject, root, userRegistry, GitHistoryUtils.LOG_ALL); } @NotNull @@ -115,11 +115,12 @@ public class GitLogProvider implements VcsLogProvider { Collection remoteBranches = repository.getBranches().getRemoteBranches(); Collection refs = new ArrayList(localBranches.size() + remoteBranches.size()); for (GitLocalBranch localBranch : localBranches) { - refs.add(myVcsObjectsFactory.createRef(HashImpl.build(localBranch.getHash()), localBranch.getName(), GitRefManager.LOCAL_BRANCH, root)); + refs.add( + myVcsObjectsFactory.createRef(HashImpl.build(localBranch.getHash()), localBranch.getName(), GitRefManager.LOCAL_BRANCH, root)); } for (GitRemoteBranch remoteBranch : remoteBranches) { refs.add(myVcsObjectsFactory.createRef(HashImpl.build(remoteBranch.getHash()), remoteBranch.getNameForLocalOperations(), - GitRefManager.REMOTE_BRANCH, root)); + GitRefManager.REMOTE_BRANCH, root)); } String currentRevision = repository.getCurrentRevision(); if (currentRevision != null) { // null => fresh repository @@ -178,9 +179,9 @@ public class GitLogProvider implements VcsLogProvider { @NotNull @Override - public List getFilteredDetails(@NotNull final VirtualFile root, - @NotNull VcsLogFilterCollection filterCollection, - int maxCount) throws VcsException { + public List getCommitsMatchingFilter(@NotNull final VirtualFile root, + @NotNull VcsLogFilterCollection filterCollection, + int maxCount) throws VcsException { if (!isRepositoryReady(root)) { return Collections.emptyList(); } @@ -188,18 +189,19 @@ public class GitLogProvider implements VcsLogProvider { List filterParameters = ContainerUtil.newArrayList(); if (filterCollection.getBranchFilter() != null) { - // git doesn't support filtering by several branches very well (--branches parameter give a weak pattern capabilities) - // => by now assuming there is only one branch filter. - if (filterCollection.getBranchFilter().getBranchNames().size() > 1) { - LOG.warn("More than one branch filter was passed. Using only the first one."); - } - String branch = filterCollection.getBranchFilter().getBranchNames().iterator().next(); GitRepository repository = getRepository(root); assert repository != null : "repository is null for root " + root + " but was previously reported as 'ready'"; - if (repository.getBranches().findBranchByName(branch) == null) { + + boolean atLeastOneBranchExists = false; + for (String branchName : filterCollection.getBranchFilter().getBranchNames()) { + if (repository.getBranches().findBranchByName(branchName) != null) { + filterParameters.add(branchName); + atLeastOneBranchExists = true; + } + } + if (!atLeastOneBranchExists) { // no such branches in this repository => filter matches nothing return Collections.emptyList(); } - filterParameters.add(branch); } else { filterParameters.addAll(GitHistoryUtils.LOG_ALL); @@ -240,7 +242,7 @@ public class GitLogProvider implements VcsLogProvider { } } - return GitHistoryUtils.getAllDetails(myProject, root, filterParameters); + return GitHistoryUtils.readCommits(myProject, root, Consumer.EMPTY_CONSUMER, filterParameters); } @Nullable diff --git a/plugins/git4idea/src/git4idea/merge/GitConflictResolver.java b/plugins/git4idea/src/git4idea/merge/GitConflictResolver.java index 2471669ce0fa..214c026307d5 100644 --- a/plugins/git4idea/src/git4idea/merge/GitConflictResolver.java +++ b/plugins/git4idea/src/git4idea/merge/GitConflictResolver.java @@ -17,13 +17,13 @@ package git4idea.merge; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.AbstractVcsHelper; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; import com.intellij.openapi.vcs.merge.MergeProvider; import com.intellij.openapi.vfs.LocalFileSystem; @@ -179,9 +179,9 @@ public class GitConflictResolver { "You should
    resolve all conflicts before update.
    " + myParams.myErrorNotificationAdditionalDescription); } - + private void notifyWarning(String title, String content) { - myPlatformFacade.getNotificator(myProject).notifyStrongWarning(title, content, new ResolveNotificationListener()); + VcsNotifier.getInstance(myProject).notifyImportantWarning(title, content, new ResolveNotificationListener()); } private boolean merge(boolean mergeDialogInvokedFromNotification) { @@ -229,11 +229,11 @@ public class GitConflictResolver { private void notifyException(VcsException e) { LOG.info("mergeFiles ", e); final String description = "Couldn't check the working tree for unmerged files because of an error."; - GitVcs.IMPORTANT_ERROR_NOTIFICATION.createNotification(myParams.myErrorNotificationTitle, - description + myParams.myErrorNotificationAdditionalDescription + "
    " + - e.getLocalizedMessage(), - NotificationType.ERROR, - new ResolveNotificationListener()).notify(myProject); + VcsNotifier.getInstance(myProject).notifyError(myParams.myErrorNotificationTitle, + description + myParams.myErrorNotificationAdditionalDescription + "
    " + + e.getLocalizedMessage(), + new ResolveNotificationListener() + ); } diff --git a/plugins/git4idea/src/git4idea/push/GitPushResult.java b/plugins/git4idea/src/git4idea/push/GitPushResult.java index 995a3a4b7058..1d105eaea0f1 100644 --- a/plugins/git4idea/src/git4idea/push/GitPushResult.java +++ b/plugins/git4idea/src/git4idea/push/GitPushResult.java @@ -20,11 +20,11 @@ import com.intellij.history.LocalHistory; import com.intellij.ide.BrowserUtil; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx; import com.intellij.openapi.vcs.update.ActionInfo; import com.intellij.openapi.vcs.update.UpdateInfoTree; @@ -32,7 +32,6 @@ import com.intellij.openapi.vcs.update.UpdatedFiles; import git4idea.GitBranch; import git4idea.GitRevisionNumber; import git4idea.GitUtil; -import git4idea.GitVcs; import git4idea.branch.GitBranchUtil; import git4idea.merge.MergeChangeCollector; import git4idea.repo.GitRepository; @@ -45,9 +44,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -/** -* @author Kirill Likhodedov -*/ class GitPushResult { private static final Logger LOG = Logger.getInstance(GitPushResult.class); @@ -221,15 +217,14 @@ class GitPushResult { } /** - * Constructs the HTML-formatted message from error outputs of failed repositories. + * Constructs the HTML-formatted message from error outputs of failed repositories and notify. * If there is only 1 repository in the project, just returns the error without writing the repository url (to avoid confusion for people * with only 1 root ever). * Otherwise adds repository URL to the error that repository produced. * * The procedure also includes collecting for updated files (if an auto-update was performed during the push), which may be lengthy. */ - @NotNull - Notification createNotification() { + void createPushNotificationAndNotify() { final UpdatedFiles updatedFiles = collectUpdatedFiles(); GroupedResult groupedResult = group(); @@ -245,7 +240,6 @@ class GitPushResult { int pushedCommitsNumber = calcPushedCommitTotalNumber(myResults); String title; - NotificationType notificationType; if (error) { if (onlyError) { title = "Push failed"; @@ -255,16 +249,13 @@ class GitPushResult { title += ", " + commits(pushedCommitsNumber) + " pushed"; } } - notificationType = NotificationType.ERROR; } else if (rejected) { if (onlyRejected) { title = "Push rejected"; } else { title = "Push partially rejected, " + commits(pushedCommitsNumber) + " pushed"; } - notificationType = NotificationType.WARNING; } else { - notificationType = NotificationType.INFORMATION; title = "Push successful"; } @@ -276,18 +267,23 @@ class GitPushResult { sb.append(errorReport); sb.append(rejectedReport); sb.append(successReport); - + if (!updatedFiles.isEmpty()) { sb.append("View files updated during the push"); } NotificationListener viewUpdateFilesListener = new ViewUpdatedFilesNotificationListener(updatedFiles); + VcsNotifier vcsNotifier = VcsNotifier.getInstance(myProject); if (onlySuccess) { - return new Notification(GitVcs.NOTIFICATION_GROUP_ID.getDisplayId(), title, sb.toString(), notificationType, viewUpdateFilesListener).setImportant(false); + vcsNotifier.notifySuccess(title, sb.toString(), viewUpdateFilesListener); + } + else if (error) { + vcsNotifier.notifyError(title, sb.toString(), viewUpdateFilesListener); + } + else { + vcsNotifier.notifyImportantWarning(title, sb.toString(), viewUpdateFilesListener); } - - return new Notification(GitVcs.IMPORTANT_ERROR_NOTIFICATION.getDisplayId(), title, sb.toString(), notificationType, viewUpdateFilesListener); } @NotNull diff --git a/plugins/git4idea/src/git4idea/push/GitPusher.java b/plugins/git4idea/src/git4idea/push/GitPusher.java index 56b3d58c7aab..31e289dba806 100644 --- a/plugins/git4idea/src/git4idea/push/GitPusher.java +++ b/plugins/git4idea/src/git4idea/push/GitPusher.java @@ -15,8 +15,6 @@ */ package git4idea.push; -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationType; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; @@ -24,6 +22,7 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ui.UIUtil; @@ -53,8 +52,6 @@ import java.util.concurrent.atomic.AtomicInteger; /** * Collects information to push and performs the push. - * - * @author Kirill Likhodedov */ public final class GitPusher { @@ -356,8 +353,7 @@ public final class GitPusher { catch (VcsException e) { LOG.error(String.format("Couldn't set up tracking for source branch %s, target branch %s, remote %s in root %s", source, dest, remote, repository), e); - Notificator.getInstance(project).notify(GitVcs.NOTIFICATION_GROUP_ID, "", "Couldn't set up branch tracking", - NotificationType.ERROR); + VcsNotifier.getInstance(project).notifyWeakError("Couldn't set up branch tracking"); } } } @@ -461,11 +457,11 @@ public final class GitPusher { result.mergeFrom(previousResult); if (result.isEmpty()) { - GitVcs.NOTIFICATION_GROUP_ID.createNotification("Nothing to push", NotificationType.INFORMATION).notify(myProject); + VcsNotifier.getInstance(myProject).notifyInfo("Nothing to push"); } else if (result.wasErrorCancelOrNotAuthorized()) { // if there was an error on any repo, we won't propose to update even if current branch of a repo was rejected - result.createNotification().notify(myProject); + result.createPushNotificationAndNotify(); } else { // there were no errors, but there might be some rejected branches on some of the repositories @@ -504,7 +500,7 @@ public final class GitPusher { } - result.createNotification().notify(myProject); + result.createPushNotificationAndNotify(); } } @@ -577,7 +573,7 @@ public final class GitPusher { description = "Push has been cancelled, because there were conflicts during update.
    " + "Check that conflicts were resolved correctly, and invoke push again."; } - new Notification(GitVcs.MINOR_NOTIFICATION.getDisplayId(), title, description, NotificationType.WARNING).notify(myProject); + VcsNotifier.getInstance(myProject).notifyMinorWarning(title, description); return false; } else { diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaser.java b/plugins/git4idea/src/git4idea/rebase/GitRebaser.java index d9869b2c0504..65883e848bd0 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaser.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaser.java @@ -127,7 +127,7 @@ public class GitRebaser { return allMerged ? GitUpdateResult.SUCCESS_WITH_RESOLVED_CONFLICTS : GitUpdateResult.INCOMPLETE; } else if (untrackedWouldBeOverwrittenDetector.wasMessageDetected()) { LOG.info("handleRebaseFailure: untracked files would be overwritten by checkout"); - UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, ServiceManager.getService(myProject, GitPlatformFacade.class), + UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, untrackedWouldBeOverwrittenDetector.getFiles(), "rebase", null); return GitUpdateResult.ERROR; } else { @@ -378,7 +378,7 @@ public class GitRebaser { return allMerged ? GitUpdateResult.SUCCESS_WITH_RESOLVED_CONFLICTS : GitUpdateResult.INCOMPLETE; } else if (untrackedWouldBeOverwrittenDetector.wasMessageDetected()) { LOG.info("handleRebaseFailure: untracked files would be overwritten by checkout"); - UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, ServiceManager.getService(myProject, GitPlatformFacade.class), + UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, untrackedWouldBeOverwrittenDetector.getFiles(), "rebase", null); return GitUpdateResult.ERROR; } else { diff --git a/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java b/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java index c06a0aec1ed7..af429ae806c8 100644 --- a/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java +++ b/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java @@ -15,132 +15,58 @@ */ package git4idea.roots; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.vcs.AbstractVcs; -import com.intellij.openapi.vcs.ProjectLevelVcsManager; -import com.intellij.openapi.vcs.VcsDirectoryMapping; -import com.intellij.openapi.vcs.VcsRoot; -import com.intellij.openapi.vcs.roots.VcsRootErrorsFinder; -import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vcs.VcsNotifier; +import com.intellij.openapi.vcs.roots.VcsIntegrationEnabler; +import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; -import git4idea.GitPlatformFacade; +import git4idea.GitUtil; import git4idea.GitVcs; -import git4idea.Notificator; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; import org.jetbrains.annotations.NotNull; -import java.util.*; +public class GitIntegrationEnabler extends VcsIntegrationEnabler { -import static com.intellij.dvcs.DvcsUtil.joinRootsPaths; -import static com.intellij.openapi.util.text.StringUtil.pluralize; - -/** - * @author Kirill Likhodedov - */ -public class GitIntegrationEnabler { - - private final @NotNull Project myProject; private final @NotNull Git myGit; - private final @NotNull GitPlatformFacade myPlatformFacade; private static final Logger LOG = Logger.getInstance(GitIntegrationEnabler.class); - public GitIntegrationEnabler(@NotNull Project project, @NotNull Git git, @NotNull GitPlatformFacade platformFacade) { - myProject = project; + public GitIntegrationEnabler(@NotNull GitVcs vcs, @NotNull Git git) { + super(vcs); myGit = git; - myPlatformFacade = platformFacade; } - public void enable(@NotNull Collection vcsRoots) { - Notificator notificator = myPlatformFacade.getNotificator(myProject); - Collection gitRoots = ContainerUtil.filter(vcsRoots, new Condition() { - @Override - public boolean value(VcsRoot root) { - AbstractVcs vcs = root.getVcs(); - return vcs != null && vcs.getName().equals(GitVcs.NAME); - } - }); - Collection roots = VcsRootErrorsFinder.vcsRootsToVirtualFiles(gitRoots); - VirtualFile projectDir = myProject.getBaseDir(); - assert projectDir != null : "Base dir is unexpectedly null for project: " + myProject; - - if (gitRoots.isEmpty()) { - boolean succeeded = gitInitOrNotifyError(notificator, projectDir); - if (succeeded) { - addVcsRoots(Collections.singleton(projectDir)); - } - } - else { - assert !roots.isEmpty(); - if (roots.size() > 1 || isProjectBelowVcs(roots)) { - notifyAddedRoots(notificator, roots); - } - addVcsRoots(roots); - } - } - - private boolean isProjectBelowVcs(@NotNull Collection gitRoots) { - //check if there are vcs roots strictly above the project dir - VirtualFile baseDir = myProject.getBaseDir(); - for (VirtualFile root : gitRoots) { - if (VfsUtilCore.isAncestor(root, baseDir, true)) { - return true; - } - } - return false; - } - - private static void notifyAddedRoots(Notificator notificator, Collection roots) { - notificator.notifySuccess("", String.format("Added Git %s: %s", pluralize("root", roots.size()), joinRootsPaths(roots))); - } - - private boolean gitInitOrNotifyError(@NotNull Notificator notificator, @NotNull final VirtualFile projectDir) { + protected boolean initOrNotifyError(@NotNull final VirtualFile projectDir) { + VcsNotifier vcsNotifier = VcsNotifier.getInstance(myProject); GitCommandResult result = myGit.init(myProject, projectDir); if (result.success()) { refreshGitDir(projectDir); - notificator.notifySuccess("", "Created Git repository in " + projectDir.getPresentableUrl()); + vcsNotifier.notifySuccess("Created Git repository in " + projectDir.getPresentableUrl()); return true; } else { - if (((GitVcs)myPlatformFacade.getVcs(myProject)).getExecutableValidator().checkExecutableAndNotifyIfNeeded()) { - notificator.notifyError("Couldn't git init " + projectDir.getPresentableUrl(), result.getErrorOutputAsHtmlString()); + if (myVcs.getExecutableValidator().checkExecutableAndNotifyIfNeeded()) { + vcsNotifier.notifyError("Couldn't git init " + projectDir.getPresentableUrl(), result.getErrorOutputAsHtmlString()); LOG.info(result.getErrorOutputAsHtmlString()); } return false; } } - private void refreshGitDir(final VirtualFile projectDir) { + private static void refreshGitDir(final VirtualFile projectDir) { UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { - myPlatformFacade.runReadAction(new Runnable() { + ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { - myPlatformFacade.getLocalFileSystem().refreshAndFindFileByPath(projectDir.getPath() + "/.git"); + LocalFileSystem.getInstance().refreshAndFindFileByPath(projectDir.getPath() + "/" + GitUtil.DOT_GIT); } }); } }); } - - private void addVcsRoots(@NotNull Collection roots) { - ProjectLevelVcsManager vcsManager = myPlatformFacade.getVcsManager(myProject); - AbstractVcs vcs = myPlatformFacade.getVcs(myProject); - List currentGitRoots = Arrays.asList(vcsManager.getRootsUnderVcs(vcs)); - - List mappings = new ArrayList(vcsManager.getDirectoryMappings(vcs)); - - for (VirtualFile root : roots) { - if (!currentGitRoots.contains(root)) { - mappings.add(new VcsDirectoryMapping(root.getPath(), vcs.getName())); - } - } - vcsManager.setDirectoryMappings(mappings); - } } diff --git a/plugins/git4idea/src/git4idea/stash/GitChangesSaver.java b/plugins/git4idea/src/git4idea/stash/GitChangesSaver.java index 0bf3c430a503..1e783918c334 100644 --- a/plugins/git4idea/src/git4idea/stash/GitChangesSaver.java +++ b/plugins/git4idea/src/git4idea/stash/GitChangesSaver.java @@ -17,16 +17,15 @@ package git4idea.stash; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.changes.ChangeListManagerEx; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.continuation.ContinuationContext; import git4idea.GitPlatformFacade; -import git4idea.GitVcs; import git4idea.commands.Git; import git4idea.config.GitVcsSettings; import git4idea.merge.GitConflictResolver; @@ -115,11 +114,14 @@ public abstract class GitChangesSaver { public void notifyLocalChangesAreNotRestored() { if (wereChangesSaved()) { LOG.info("Update is incomplete, changes are not restored"); - GitVcs.IMPORTANT_ERROR_NOTIFICATION.createNotification("Local changes were not restored", - "Before update your uncommitted changes were saved to " + getSaverName() + ".
    " + - "Update is not complete, you have unresolved merges in your working tree
    " + - "Resolve conflicts, complete update and restore changes manually.", NotificationType.WARNING, - new ShowSavedChangesNotificationListener()).notify(myProject); + VcsNotifier.getInstance(myProject).notifyImportantWarning("Local changes were not restored", + "Before update your uncommitted changes were saved to " + + getSaverName() + + ".
    " + + "Update is not complete, you have unresolved merges in your working tree
    " + + "Resolve conflicts, complete update and restore changes manually.", + new ShowSavedChangesNotificationListener() + ); } } diff --git a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java index aa4ea51c68af..8e20a67fceab 100644 --- a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java +++ b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java @@ -17,11 +17,11 @@ package git4idea.stash; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.impl.LocalChangesUnderRoots; @@ -30,7 +30,6 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.continuation.ContinuationContext; import git4idea.GitPlatformFacade; -import git4idea.GitVcs; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; import git4idea.commands.GitHandlerUtil; @@ -179,24 +178,30 @@ public class GitStashChangesSaver extends GitChangesSaver { @Override protected void notifyUnresolvedRemain() { - GitVcs.IMPORTANT_ERROR_NOTIFICATION.createNotification("Local changes were restored with conflicts", - "Your uncommitted changes were saved to stash.
    " + - "Unstash is not complete, you have unresolved merges in your working tree
    " + - "Resolve conflicts and drop the stash.", - NotificationType.WARNING, new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - if (event.getDescription().equals("saver")) { - // we don't use #showSavedChanges to specify unmerged root first - GitUnstashDialog.showUnstashDialog(myProject, new ArrayList(myStashedRoots), myStashedRoots.iterator().next() - ); - } else if (event.getDescription().equals("resolve")) { - mergeNoProceed(); - } - } - } - }).notify(myProject); + VcsNotifier.getInstance(myProject).notifyImportantWarning("Local changes were restored with conflicts", + "Your uncommitted changes were saved to stash.
    " + + "Unstash is not complete, you have unresolved merges in your working tree
    " + + "Resolve conflicts and drop the stash.", + new NotificationListener() { + @Override + public void hyperlinkUpdate(@NotNull Notification notification, + @NotNull HyperlinkEvent event) { + if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + if (event.getDescription().equals("saver")) { + // we don't use #showSavedChanges to specify unmerged root first + GitUnstashDialog.showUnstashDialog(myProject, + new ArrayList( + myStashedRoots), + myStashedRoots.iterator().next() + ); + } + else if (event.getDescription().equals("resolve")) { + mergeNoProceed(); + } + } + } + } + ); } } diff --git a/plugins/git4idea/src/git4idea/ui/GitTagDialog.java b/plugins/git4idea/src/git4idea/ui/GitTagDialog.java index aafb3b05db0f..e5060ae2a77b 100644 --- a/plugins/git4idea/src/git4idea/ui/GitTagDialog.java +++ b/plugins/git4idea/src/git4idea/ui/GitTagDialog.java @@ -20,6 +20,7 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.DocumentAdapter; import git4idea.GitUtil; @@ -196,7 +197,8 @@ public class GitTagDialog extends DialogWrapper { } try { GitHandlerUtil.doSynchronously(h, GitBundle.getString("tagging.title"), h.printableCommandLine()); - GitUIUtil.notifySuccess(myProject, myTagNameTextField.getText(), "Created tag " + myTagNameTextField.getText() + " successfully."); + VcsNotifier.getInstance(myProject).notifySuccess(myTagNameTextField.getText(), + "Created tag " + myTagNameTextField.getText() + " successfully."); } finally { exceptions.addAll(h.errors()); diff --git a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java index a6790ddb3a5f..63b51a8c4b56 100644 --- a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java +++ b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java @@ -19,7 +19,6 @@ import com.intellij.CommonBundle; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.components.ServiceManager; @@ -32,12 +31,16 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Key; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.DocumentAdapter; import com.intellij.util.Consumer; -import git4idea.*; +import git4idea.GitPlatformFacade; +import git4idea.GitRevisionNumber; +import git4idea.GitUtil; +import git4idea.GitVcs; import git4idea.branch.GitBranchUtil; import git4idea.commands.*; import git4idea.config.GitVersionSpecialty; @@ -442,10 +445,9 @@ public class GitUnstashDialog extends DialogWrapper { @Override protected void notifyUnresolvedRemain() { - GitVcs.IMPORTANT_ERROR_NOTIFICATION.createNotification("Conflicts were not resolved during unstash", - "Unstash is not complete, you have unresolved merges in your working tree
    " + - "Resolve conflicts.", - NotificationType.WARNING, new NotificationListener() { + VcsNotifier.getInstance(myProject).notifyImportantWarning("Conflicts were not resolved during unstash", + "Unstash is not complete, you have unresolved merges in your working tree
    " + + "Resolve conflicts.", new NotificationListener() { @Override public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { @@ -454,7 +456,8 @@ public class GitUnstashDialog extends DialogWrapper { } } } - }).notify(myProject); + } + ); } } diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java index 94f49ec9aa95..645fb24bcd1e 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java @@ -20,7 +20,6 @@ import com.intellij.dvcs.ui.BranchActionGroupPopup; import com.intellij.dvcs.ui.RootAction; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DefaultActionGroup; @@ -28,6 +27,7 @@ import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.ui.popup.list.ListPopupImpl; import git4idea.GitUtil; import git4idea.GitVcs; @@ -158,20 +158,24 @@ class GitBranchPopup { } private void notifyAboutSyncedBranches() { - GitVcs.IMPORTANT_ERROR_NOTIFICATION.createNotification("Synchronous branch control enabled", - "You have several Git roots in the project and they all are checked out at the same branch. " + - "We've enabled synchronous branch control for the project.
    " + - "If you wish to control branches in different roots separately, you may disable the setting.", - NotificationType.INFORMATION, new NotificationListener() { - @Override public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - ShowSettingsUtil.getInstance().showSettingsDialog(myProject, myVcs.getConfigurable().getDisplayName()); - if (myVcsSettings.getSyncSetting() == GitBranchSyncSetting.DONT) { - notification.expire(); - } - } - } - }).notify(myProject); + VcsNotifier.getInstance(myProject).notifyImportantInfo("Synchronous branch control enabled", + "You have several Git roots in the project and they all are checked out at the same branch. " + + "We've enabled synchronous branch control for the project.
    " + + "If you wish to control branches in different roots separately, you may disable the setting.", + new NotificationListener() { + @Override + public void hyperlinkUpdate(@NotNull Notification notification, + @NotNull HyperlinkEvent event) { + if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + ShowSettingsUtil.getInstance().showSettingsDialog(myProject, myVcs + .getConfigurable().getDisplayName()); + if (myVcsSettings.getSyncSetting() == GitBranchSyncSetting.DONT) { + notification.expire(); + } + } + } + } + ); } private ActionGroup createActions() { diff --git a/plugins/git4idea/src/git4idea/update/GitFetcher.java b/plugins/git4idea/src/git4idea/update/GitFetcher.java index edac044604a7..71111366bdbd 100644 --- a/plugins/git4idea/src/git4idea/update/GitFetcher.java +++ b/plugins/git4idea/src/git4idea/update/GitFetcher.java @@ -16,15 +16,18 @@ package git4idea.update; import com.intellij.dvcs.DvcsUtil; -import com.intellij.notification.NotificationType; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vfs.VirtualFile; -import git4idea.*; +import git4idea.GitLocalBranch; +import git4idea.GitRemoteBranch; +import git4idea.GitUtil; +import git4idea.GitVcs; import git4idea.branch.GitBranchUtil; import git4idea.commands.*; import git4idea.config.GitVersionSpecialty; @@ -48,9 +51,6 @@ import java.util.regex.Pattern; import static git4idea.GitBranch.REFS_HEADS_PREFIX; import static git4idea.GitBranch.REFS_REMOTES_PREFIX; -/** - * @author Kirill Likhodedov - */ public class GitFetcher { private static final Logger LOG = Logger.getInstance(GitFetcher.class); @@ -285,9 +285,9 @@ public class GitFetcher { @NotNull GitFetchResult result, @Nullable String errorNotificationTitle, @NotNull Collection errors) { if (result.isSuccess()) { - GitVcs.NOTIFICATION_GROUP_ID.createNotification("Fetched successfully" + result.getAdditionalInfo(), NotificationType.INFORMATION).notify(project); + VcsNotifier.getInstance(project).notifySuccess("Fetched successfully" + result.getAdditionalInfo()); } else if (result.isCancelled()) { - GitVcs.NOTIFICATION_GROUP_ID.createNotification("Fetch cancelled by user" + result.getAdditionalInfo(), NotificationType.WARNING).notify(project); + VcsNotifier.getInstance(project).notifyMinorWarning("", "Fetch cancelled by user" + result.getAdditionalInfo()); } else if (result.isNotAuthorized()) { String title; String description; @@ -299,11 +299,11 @@ public class GitFetcher { description = "Couldn't authorize"; } description += result.getAdditionalInfo(); - GitUIUtil.notifyMessage(project, title, description, NotificationType.ERROR, true, null); + GitUIUtil.notifyMessage(project, title, description, true, null); } else { GitVcs instance = GitVcs.getInstance(project); if (instance != null && instance.getExecutableValidator().isExecutableValid()) { - GitUIUtil.notifyMessage(project, "Fetch failed", result.getAdditionalInfo(), NotificationType.ERROR, true, errors); + GitUIUtil.notifyMessage(project, "Fetch failed", result.getAdditionalInfo(), true, errors); } } } @@ -336,12 +336,12 @@ public class GitFetcher { } } if (notifySuccess) { - GitUIUtil.notifySuccess(myProject, "", "Fetched successfully"); + VcsNotifier.getInstance(myProject).notifySuccess("Fetched successfully"); } String addInfo = makeAdditionalInfoByRoot(additionalInfo); if (!StringUtil.isEmptyOrSpaces(addInfo)) { - Notificator.getInstance(myProject).notify(GitVcs.MINOR_NOTIFICATION, "Fetch details", addInfo, NotificationType.INFORMATION); + VcsNotifier.getInstance(myProject).notifyMinorInfo("Fetch details", addInfo); } return true; diff --git a/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java b/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java index 6221527d79be..4815eb781fce 100644 --- a/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java +++ b/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java @@ -30,7 +30,6 @@ import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ui.UIUtil; import git4idea.GitUtil; -import git4idea.GitPlatformFacade; import git4idea.branch.GitBranchPair; import git4idea.commands.*; import git4idea.merge.GitConflictResolver; @@ -136,7 +135,7 @@ public class GitMergeUpdater extends GitUpdater { } else if (untrackedFilesWouldBeOverwrittenByMergeDetector.wasMessageDetected()) { LOG.info("handleMergeFailure: untracked files would be overwritten by merge"); - UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, ServiceManager.getService(myProject, GitPlatformFacade.class), + UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, untrackedFilesWouldBeOverwrittenByMergeDetector.getFiles(), "merge", null); return GitUpdateResult.ERROR; } diff --git a/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java b/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java index b314cc914a41..04ed3ac845ca 100644 --- a/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java +++ b/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java @@ -20,12 +20,12 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ui.UIUtil; import git4idea.GitBranch; import git4idea.GitUtil; -import git4idea.Notificator; import git4idea.branch.GitBranchPair; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; @@ -147,10 +147,11 @@ public class GitRebaseUpdater extends GitUpdater { // this is not critical, and update has already happened, // so we just notify the user about problems with collecting the updated changes. LOG.info("Couldn't mark end for repository " + myRoot, e); - Notificator.getInstance(myProject). - notifyWeakWarning("Couldn't collect the updated files info", - String.format("Update of %s was successful, but we couldn't collect the updated changes because of an error", - myRoot), null); + VcsNotifier.getInstance(myProject). + notifyMinorWarning("Couldn't collect the updated files info", + String.format("Update of %s was successful, but we couldn't collect the updated changes because of an error", + myRoot), null + ); } return result.success(); } diff --git a/plugins/git4idea/src/git4idea/util/GitPreservingProcess.java b/plugins/git4idea/src/git4idea/util/GitPreservingProcess.java index b95d480ada24..b42c1133e89c 100644 --- a/plugins/git4idea/src/git4idea/util/GitPreservingProcess.java +++ b/plugins/git4idea/src/git4idea/util/GitPreservingProcess.java @@ -22,13 +22,13 @@ import com.intellij.openapi.util.Clock; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.text.DateFormatUtil; import git4idea.GitPlatformFacade; import git4idea.GitUtil; -import git4idea.Notificator; import git4idea.commands.Git; import git4idea.merge.GitConflictResolver; import git4idea.repo.GitRepository; @@ -155,7 +155,7 @@ public class GitPreservingProcess { return true; } catch (VcsException e) { LOG.info("Couldn't save local changes", e); - Notificator.getInstance(myProject).notifyError( + VcsNotifier.getInstance(myProject).notifyError( "Couldn't save uncommitted changes.", String.format("Tried to save uncommitted changes in stash before %s, but failed with an error.
    %s", myOperationTitle, join(e.getMessages()))); @@ -174,8 +174,9 @@ public class GitPreservingProcess { } catch (VcsException e) { LOG.info("Couldn't load local changes", e); - Notificator.getInstance(myProject).notifyError("Couldn't restore uncommitted changes", - String.format("Tried to unstash uncommitted changes, but failed with error.
    %s",join(e.getMessages()))); + VcsNotifier.getInstance(myProject).notifyError("Couldn't restore uncommitted changes", + String.format("Tried to unstash uncommitted changes, but failed with error.
    %s", + join(e.getMessages()))); } } } diff --git a/plugins/git4idea/src/git4idea/util/GitUIUtil.java b/plugins/git4idea/src/git4idea/util/GitUIUtil.java index ae44f756a8cd..71a818d186b8 100644 --- a/plugins/git4idea/src/git4idea/util/GitUIUtil.java +++ b/plugins/git4idea/src/git4idea/util/GitUIUtil.java @@ -15,19 +15,16 @@ */ package git4idea.util; -import com.intellij.notification.NotificationGroup; -import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.AbstractVcsHelper; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.ListCellRendererWrapper; import git4idea.GitBranch; import git4idea.GitUtil; -import git4idea.GitVcs; import git4idea.i18n.GitBundle; import git4idea.repo.GitRepository; import org.jetbrains.annotations.NonNls; @@ -55,51 +52,55 @@ public class GitUIUtil { /** * A private constructor for utility class */ - private GitUIUtil() { } - - public static void notify(@NotNull NotificationGroup group, @NotNull Project project, @Nullable String title, @Nullable String description, @NotNull NotificationType type, @Nullable NotificationListener listener) { - // title can't be null, but can be empty; description can't be neither null, nor empty - if (title == null) { - title = ""; - } - if (StringUtil.isEmptyOrSpaces(description)) { - description = title; - title = ""; - } - // if both title and description were empty, then it is a problem in the calling code => let assertion notify about that. - assert description != null : "description is null after StringUtil.isEmptyOrSpaces(). title: " + title; - group.createNotification(title, description, type, listener).notify(project.isDefault() ? null : project); + private GitUIUtil() { } - public static void notifyMessages(Project project, @Nullable String title, @Nullable String description, NotificationType type, boolean important, @Nullable Collection messages) { + public static void notifyMessages(@NotNull Project project, + @NotNull String title, + @Nullable String description, + boolean important, + @Nullable Collection messages) { String desc = (description != null ? description.replace("\n", "
    ") : ""); if (messages != null && !messages.isEmpty()) { desc += StringUtil.join(messages, "

    "); } - NotificationGroup group = important ? GitVcs.IMPORTANT_ERROR_NOTIFICATION : GitVcs.NOTIFICATION_GROUP_ID; - notify(group, project, title, desc, type, null); + VcsNotifier notificator = VcsNotifier.getInstance(project); + if (important) { + notificator.notifyError(title, desc); + } + else { + notificator.notifyImportantWarning(title, desc, null); + } } - public static void notifyMessage(Project project, @Nullable String title, @Nullable String description, NotificationType type, boolean important, @Nullable Collection errors) { + public static void notifyMessage(Project project, + @NotNull String title, + @Nullable String description, + boolean important, + @Nullable Collection errors) { Collection errorMessages; if (errors == null) { errorMessages = null; - } else { + } + else { errorMessages = new HashSet(errors.size()); for (Exception error : errors) { if (error instanceof VcsException) { for (String message : ((VcsException)error).getMessages()) { errorMessages.add(message.replace("\n", "
    ")); } - } else { + } + else { errorMessages.add(error.toString().replace("\n", "
    ")); } } } - notifyMessages(project, title, description, type, important, errorMessages); + notifyMessages(project, title, description, important, errorMessages); } - public static void notifyError(Project project, String title, String description, boolean important, @Nullable Exception error) { notifyMessage(project, title, description, NotificationType.ERROR, important, Collections.singleton(error)); + public static void notifyError(Project project, String title, String description, boolean important, @Nullable Exception error) { + notifyMessage( + project, title, description, important, Collections.singleton(error)); } /** @@ -119,19 +120,8 @@ public class GitUIUtil { return content.toString(); } - /** - * Displays a "success"-notification. - */ - public static void notifySuccess(Project project, String title, String description) { - GitVcs.NOTIFICATION_GROUP_ID.createNotification(title, description, NotificationType.INFORMATION, null).notify(project); - } - - public static void notifyError(Project project, String title, String description) { - notifyMessage(project, title, description, NotificationType.ERROR, false, null); - } - public static void notifyImportantError(Project project, String title, String description) { - notifyMessage(project, title, description, NotificationType.ERROR, true, null); + notifyMessage(project, title, description, true, null); } public static void notifyGitErrors(Project project, String title, String description, Collection gitErrors) { @@ -145,7 +135,7 @@ public class GitUIUtil { for (VcsException e : gitErrors) { content.append(e.getLocalizedMessage()).append("
    "); } - notifyError(project, title, content.toString()); + notifyMessage(project, title, content.toString(), false, null); } /** diff --git a/plugins/git4idea/src/git4idea/util/UntrackedFilesNotifier.java b/plugins/git4idea/src/git4idea/util/UntrackedFilesNotifier.java index 55d6a63cbc15..6a867933e6aa 100644 --- a/plugins/git4idea/src/git4idea/util/UntrackedFilesNotifier.java +++ b/plugins/git4idea/src/git4idea/util/UntrackedFilesNotifier.java @@ -19,9 +19,9 @@ import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.changes.ui.SelectFilesDialog; import com.intellij.openapi.vfs.VirtualFile; -import git4idea.GitPlatformFacade; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,9 +30,6 @@ import javax.swing.event.HyperlinkEvent; import java.util.ArrayList; import java.util.Collection; -/** - * @author Kirill Likhodedov - */ public class UntrackedFilesNotifier { private UntrackedFilesNotifier() { @@ -44,13 +41,13 @@ public class UntrackedFilesNotifier { * @param operation the name of the Git operation that caused the error: {@code rebase, merge, checkout}. * @param description the content of the notification or null if the deafult content is to be used. */ - public static void notifyUntrackedFilesOverwrittenBy(@NotNull final Project project, @NotNull GitPlatformFacade platformFacade, + public static void notifyUntrackedFilesOverwrittenBy(@NotNull final Project project, @NotNull final Collection untrackedFiles, @NotNull final String operation, @Nullable String description) { final String notificationTitle = StringUtil.capitalize(operation) + " failed"; final String notificationDesc = description == null ? createUntrackedFilesOverwrittenDescription(operation, true) : description; - platformFacade.getNotificator(project).notifyError(notificationTitle, notificationDesc, + VcsNotifier.getInstance(project).notifyError(notificationTitle, notificationDesc, new NotificationListener() { @Override public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { diff --git a/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java b/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java index 89dfd4481245..0fc9dc3464cf 100644 --- a/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java +++ b/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java @@ -6,9 +6,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vcs.AbstractVcs; -import com.intellij.openapi.vcs.AbstractVcsHelper; -import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.ChangeListManagerImpl; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; @@ -29,7 +27,6 @@ import git4idea.remote.GitHttpAuthTestService; import git4idea.repo.GitRepository; import git4idea.test.GitExecutor; import git4idea.test.GitTestUtil; -import git4idea.test.TestNotificator; import org.jetbrains.annotations.NotNull; import org.junit.Assert; import org.picocontainer.MutablePicoContainer; @@ -52,8 +49,6 @@ import static org.junit.Assume.assumeTrue; /** *

    The container of test environment variables which should be visible from any step definition script.

    *

    Most of the fields are populated in the Before hook of the {@link GeneralStepdefs}.

    - * - * @author Kirill Likhodedov */ public class GitCucumberWorld { @@ -70,7 +65,7 @@ public class GitCucumberWorld { public static GitVcs myVcs; public static MockVcsHelper myVcsHelper; - public static TestNotificator myNotificator; + public static TestVcsNotifier myNotificator; public static GitHttpAuthTestService myHttpAuthService; // only with @remote tag @@ -114,7 +109,7 @@ public class GitCucumberWorld { // because MockVcsHelper is not ready to be a full featured implementation for all tests. myVcsHelper = overrideService(myProject, AbstractVcsHelper.class, MockVcsHelper.class); myChangeListManager = (ChangeListManagerImpl)myPlatformFacade.getChangeListManager(myProject); - myNotificator = (TestNotificator)ServiceManager.getService(myProject, Notificator.class); + myNotificator = (TestVcsNotifier)ServiceManager.getService(myProject, VcsNotifier.class); myVcs = GitVcs.getInstance(myProject); virtualCommits = new GitTestVirtualCommitsHolder(); diff --git a/plugins/git4idea/testFramework/META-INF/plugin.xml b/plugins/git4idea/testFramework/META-INF/plugin.xml index 6ecf51797de2..18eb7ed6b97b 100644 --- a/plugins/git4idea/testFramework/META-INF/plugin.xml +++ b/plugins/git4idea/testFramework/META-INF/plugin.xml @@ -14,8 +14,8 @@ serviceImplementation="git4idea.test.TestDialogManager" overrides="true" /> - diff --git a/plugins/git4idea/testFramework/git4idea/test/GitTestPlatformFacade.groovy b/plugins/git4idea/testFramework/git4idea/test/GitTestPlatformFacade.groovy index 07f4a33c5aab..40c1200c28ec 100644 --- a/plugins/git4idea/testFramework/git4idea/test/GitTestPlatformFacade.groovy +++ b/plugins/git4idea/testFramework/git4idea/test/GitTestPlatformFacade.groovy @@ -14,27 +14,21 @@ * limitations under the License. */ package git4idea.test - import com.intellij.dvcs.test.DvcsTestPlatformFacade import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper import com.intellij.openapi.vcs.AbstractVcs import com.intellij.openapi.vcs.ProjectLevelVcsManager import git4idea.GitPlatformFacade -import git4idea.Notificator import git4idea.config.GitVcsApplicationSettings import git4idea.config.GitVcsSettings import org.jetbrains.annotations.NotNull -/** - * - * @author Kirill Likhodedov - */ + class GitTestPlatformFacade extends DvcsTestPlatformFacade implements GitPlatformFacade { private static final GitVcsApplicationSettings ourAppSettings = new GitVcsApplicationSettings() private GitMockVcs myVcs; - private TestNotificator myNotificator; private TestDialogManager myTestDialogManager; private GitTestRepositoryManager myRepositoryManager; private GitMockVcsManager myVcsManager; @@ -54,15 +48,6 @@ class GitTestPlatformFacade extends DvcsTestPlatformFacade implements GitPlatfor return myVcsManager; } - @NotNull - @Override - public Notificator getNotificator(@NotNull Project project) { - if (myNotificator == null) { - myNotificator = new TestNotificator(project); - } - return myNotificator; - } - @NotNull @Override public GitVcsSettings getSettings(Project project) { diff --git a/plugins/git4idea/testFramework/git4idea/test/GitTestUtil.java b/plugins/git4idea/testFramework/git4idea/test/GitTestUtil.java index e5db2c9adfbd..695e2dd1dc59 100644 --- a/plugins/git4idea/testFramework/git4idea/test/GitTestUtil.java +++ b/plugins/git4idea/testFramework/git4idea/test/GitTestUtil.java @@ -16,17 +16,16 @@ package git4idea.test; import com.intellij.notification.Notification; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.TestVcsNotifier; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitUtil; import git4idea.GitVcs; -import git4idea.Notificator; import git4idea.repo.GitRepository; -import junit.framework.Assert; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.ide.BuiltInServerManagerImpl; @@ -41,11 +40,8 @@ import static com.intellij.openapi.vcs.VcsTestUtil.createDir; import static com.intellij.openapi.vcs.VcsTestUtil.createFile; import static git4idea.test.GitExecutor.git; import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; +import static junit.framework.TestCase.assertEquals; -/** - * @author Kirill Likhodedov - */ public class GitTestUtil { private static final String USER_NAME = "John Doe"; @@ -115,17 +111,27 @@ public class GitTestUtil { return repository; } - public static void assertNotification(@NotNull Project project, @Nullable Notification expected) { - if (expected == null) { - assertNull("Notification is unexpected here", expected); - return; + public static void assertNotificationShown(@NotNull Project project, @Nullable Notification expected) { + if (expected != null) { + Notification actualNotification = + ((TestVcsNotifier)VcsNotifier.getInstance(project)).getLastNotification(); + assertNotNull("No notification was shown", actualNotification); + assertEquals("Notification has wrong title", expected.getTitle(), actualNotification.getTitle()); + assertEquals("Notification has wrong type", expected.getType(), actualNotification.getType()); + assertEquals("Notification has wrong content", adjustTestContent(expected.getContent()), actualNotification.getContent()); + } + } + + // we allow more spaces and line breaks in tests to make them more readable. + // After all, notifications display html, so all line breaks and extra spaces are ignored. + private static String adjustTestContent(@NotNull String s) { + StringBuilder res = new StringBuilder(); + String[] splits = s.split("\n"); + for (String split : splits) { + res.append(split.trim()); } - Notification actualNotification = ((TestNotificator)ServiceManager.getService(project, Notificator.class)).getLastNotification(); - Assert.assertNotNull("No notification was shown", actualNotification); - Assert.assertEquals("Notification has wrong title", expected.getTitle(), actualNotification.getTitle()); - Assert.assertEquals("Notification has wrong type", expected.getType(), actualNotification.getType()); - Assert.assertEquals("Notification has wrong content", expected.getContent(), actualNotification.getContent()); + return res.toString(); } /** diff --git a/plugins/git4idea/testFramework/git4idea/test/TestNotificator.java b/plugins/git4idea/testFramework/git4idea/test/TestNotificator.java deleted file mode 100644 index e8ad4f749191..000000000000 --- a/plugins/git4idea/testFramework/git4idea/test/TestNotificator.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package git4idea.test; - -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationGroup; -import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; -import com.intellij.openapi.project.Project; -import git4idea.Notificator; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author Kirill Likhodedov - */ -public class TestNotificator extends Notificator { - - public static final String TEST_NOTIFICATION_GROUP = "Test"; - private Notification myLastNotification; - - public TestNotificator(@NotNull Project project) { - super(project); - } - - public Notification getLastNotification() { - return myLastNotification; - } - - public void notify(@NotNull Notification notification) { - myLastNotification = notification; - } - - public void notify(@NotNull NotificationGroup notificationGroup, @NotNull String title, @NotNull String message, @NotNull NotificationType type) { - notify(notificationGroup, title, message, type, null); - } - - @Override - public void notifyError(@NotNull String title, @NotNull String message, @Nullable NotificationListener listener) { - myLastNotification = createNotification(title, message, NotificationType.ERROR); - } - - @Override - public void notifySuccess(@NotNull String title, @NotNull String message) { - myLastNotification = createNotification(title, message, NotificationType.INFORMATION); - } - - @Override - public void notifyWeakWarning(@NotNull String title, @NotNull String message, @Nullable NotificationListener listener) { - myLastNotification = createNotification(title, message, NotificationType.WARNING); - } - - @Override - public void notifyStrongWarning(@NotNull String title, @NotNull String content, @Nullable NotificationListener listener) { - myLastNotification = createNotification(title, content, NotificationType.WARNING); - } - - @NotNull - private static Notification createNotification(@NotNull String title, @NotNull String message, NotificationType type) { - return new Notification(TEST_NOTIFICATION_GROUP, title, message, type); - } - - public void notify(@NotNull NotificationGroup notificationGroup, @NotNull String title, @NotNull String message, - @NotNull NotificationType type, @Nullable NotificationListener listener) { - myLastNotification = createNotification(notificationGroup, title, message, type, listener); - } - - public void cleanup() { - myLastNotification = null; - } -} diff --git a/plugins/git4idea/tests/git4idea/roots/GitIntegrationEnablerTest.java b/plugins/git4idea/tests/git4idea/roots/GitIntegrationEnablerTest.java index 37b1d926303a..81a95b670d10 100644 --- a/plugins/git4idea/tests/git4idea/roots/GitIntegrationEnablerTest.java +++ b/plugins/git4idea/tests/git4idea/roots/GitIntegrationEnablerTest.java @@ -15,110 +15,106 @@ */ package git4idea.roots; -import com.intellij.dvcs.test.MockVirtualFile; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsRoot; import com.intellij.openapi.vcs.VcsTestUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.PlatformTestCase; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcsUtil.VcsUtil; import git4idea.GitUtil; -import git4idea.test.GitLightTest; -import git4idea.test.TestNotificator; +import git4idea.GitVcs; +import git4idea.commands.Git; +import git4idea.test.GitTestUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; import java.io.File; -import java.util.*; +import java.util.Collection; +import java.util.List; -import static org.junit.Assert.*; -/** - * @author Nadya Zabrodina - */ -public class GitIntegrationEnablerTest extends GitLightTest { +public class GitIntegrationEnablerTest extends UsefulTestCase { - public AbstractVcs myVcs; + public Project myProject; + protected VirtualFile myProjectRoot; + protected VirtualFile myTestRoot; + public GitVcs myVcs; + public Git myGit; - @Override - @Before - public void setUp() { + + @SuppressWarnings("JUnitTestCaseWithNonTrivialConstructors") + public GitIntegrationEnablerTest() { + PlatformTestCase.initPlatformLangPrefix(); + } + + public void setUp() throws Exception { super.setUp(); - myVcs = myPlatformFacade.getVcs(myProject); + IdeaProjectTestFixture projectFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getTestName(true)).getFixture(); + projectFixture.setUp(); + myProject = projectFixture.getProject(); + myVcs = GitVcs.getInstance(myProject); + myProjectRoot = myProject.getBaseDir(); + myTestRoot = myProjectRoot.getParent(); + myGit = ServiceManager.getService(myProject, Git.class); } - @After - public void tearDown() { - super.tearDown(); + public void testOneRootForTheWholeProjectThenJustAddVcsRoot() { + doTest(given("."), null, null); } - @Test - public void oneRootForTheWholeProjectThenJustAddVcsrRoot() { - Map> map = new HashMap>(); - map.put("git_init", Collections.emptyList()); - doTest(given(Arrays.asList(".")), - map, null); + public void testNoGitRootsThenInitAndNotify() { + doTest(given(), + notification("Created Git repository in " + myProjectRoot.getPresentableUrl()), ".", VcsTestUtil.toAbsolute(".", myProject)); } - @Test - public void noGitRootsThenInitAndNotify() { - Map> map = new HashMap>(); - map.put("git_init", Arrays.asList(".")); - map.put("vcs_roots", VcsTestUtil.toAbsolute(Arrays.asList("."), myProject)); - - - doTest(given(Collections.emptyList()), - map, notification("Created Git repository in " + myProjectRoot)); + public void testBelowGitNoInsideThenNotify() { + doTest(given(".."), + notification("Added Git root: " + myTestRoot.getPresentableUrl())); } - @Test - public void belowGitNoInsideThenNotify() { - Map> map = new HashMap>(); - map.put("git_init", Collections.emptyList()); - - doTest(given(Arrays.asList("..")), - map, notification("Added Git root: " + myTestRoot)); + public void testGitForProjectSomeInsideThenNotify() { + doTest(given(".", "community"), + notification("Added Git roots: " + myProjectRoot.getPresentableUrl() + ", " + getPresentationForRoot("community"))); } - @Test - public void gitForProjectSomeInsideThenNotify() { - Map> map = new HashMap>(); - map.put("git_init", Collections.emptyList()); - - doTest(given(Arrays.asList(".", "community")), - map, notification("Added Git roots: " + myProjectRoot + ", " + getPresentationForRoot("community"))); + public void testBelowGitSomeInsideThenNotify() { + doTest(given("..", "community"), + notification("Added Git roots: " + myTestRoot.getPresentableUrl() + ", " + getPresentationForRoot("community"))); } - @Test - public void belowGitSomeInsideThenNotify() { - Map> map = new HashMap>(); - map.put("git_init", Collections.emptyList()); - - doTest(given(Arrays.asList("..", "community")), - map, notification("Added Git roots: " + myTestRoot + ", " + getPresentationForRoot("community"))); + public void testNotUnderGitSomeInsideThenNotify() { + doTest(given("community", "contrib"), + notification( + "Added Git roots: " + getPresentationForRoot("community") + ", " + getPresentationForRoot("contrib")) + ); } - @Test - public void notUnderGitSomeInsideThenNotify() { - Map> map = new HashMap>(); - map.put("git_init", Collections.emptyList()); + private void doTest(@NotNull Collection vcsRoots, + @Nullable Notification notification - doTest(given(Arrays.asList("community", "contrib")), - map, notification( - "Added Git roots: " + getPresentationForRoot("community") + ", " + getPresentationForRoot("contrib"))); + ) { + doTest(vcsRoots, notification, null); } - private void doTest(@NotNull Collection vcsRoots, @NotNull Map> map, @Nullable Notification notification) { + private void doTest(@NotNull Collection vcsRoots, + @Nullable Notification notification, + @Nullable String git_init, + @NotNull String... vcs_roots) { + List vcsRootsList = ContainerUtil.newArrayList(vcs_roots); //default - if (map.get("vcs_roots") == null) { - map.put("vcs_roots", ContainerUtil.map(vcsRoots, new Function() { + if (vcsRootsList.isEmpty()) { + vcsRootsList.addAll(ContainerUtil.map(vcsRoots, new Function() { @Override public String fun(VcsRoot root) { @@ -127,32 +123,30 @@ public class GitIntegrationEnablerTest extends GitLightTest { } })); } - - new GitIntegrationEnabler(myProject, myGit, myPlatformFacade).enable(vcsRoots); - - assertVcsRoots(map.get("vcs_roots")); - assertGitInit(map.get("git_init")); - assertNotificationShown(notification); + new GitIntegrationEnabler(myVcs, myGit).enable(vcsRoots); + assertVcsRoots(vcsRootsList); + if (git_init != null) { + assertGitInit(git_init); + } + GitTestUtil.assertNotificationShown(myProject, notification); } - void assertGitInit(@NotNull Collection roots) { - for (String root : roots) { - File rootFile = new File(myProjectRoot, root); - assertTrue(new File(rootFile.getPath(), GitUtil.DOT_GIT).exists()); - } + void assertGitInit(@NotNull String root) { + File rootFile = new File(myProjectRoot.getPath(), root); + assertTrue(new File(rootFile.getPath(), GitUtil.DOT_GIT).exists()); } void assertVcsRoots(@NotNull Collection expectedVcsRoots) { - VirtualFile[] actualRoots = myPlatformFacade.getVcsManager(myProject).getRootsUnderVcs(myPlatformFacade.getVcs(myProject)); + List actualRoots = ProjectLevelVcsManager.getInstance(myProject).getRootsUnderVcsWithoutFiltering(myVcs); VcsTestUtil.assertEqualCollections(expectedVcsRoots, getPaths(actualRoots)); } - Collection given(@NotNull Collection roots) { + private Collection given(@NotNull String... roots) { return ContainerUtil.map(roots, new Function() { @Override public VcsRoot fun(String s) { - return new VcsRoot(myVcs, new MockVirtualFile(VcsTestUtil.toAbsolute(s, myProject))); + return new VcsRoot(myVcs, VcsUtil.getVirtualFile(VcsTestUtil.toAbsolute(s, myProject))); } }); } @@ -161,30 +155,8 @@ public class GitIntegrationEnablerTest extends GitLightTest { return new Notification("Test", "", content, NotificationType.INFORMATION); } - void assertNotificationShown(@Nullable Notification expected) { - if (expected != null) { - Notification actualNotification = ((TestNotificator)myPlatformFacade.getNotificator(myProject)).getLastNotification(); - assertNotNull("No notification was shown", actualNotification); - assertEquals("Notification has wrong title", expected.getTitle(), actualNotification.getTitle()); - assertEquals("Notification has wrong type", expected.getType(), actualNotification.getType()); - assertEquals("Notification has wrong content", adjustTestContent(expected.getContent()), actualNotification.getContent()); - } - } - - // we allow more spaces and line breaks in tests to make them more readable. - // After all, notifications display html, so all line breaks and extra spaces are ignored. - String adjustTestContent(@NotNull String s) { - StringBuilder res = new StringBuilder(); - String[] splits = s.split("\n"); - for (String split : splits) { - res.append(split.trim()); - } - - return res.toString(); - } - @NotNull - public static Collection getPaths(@NotNull VirtualFile[] virtualFiles) { + public static Collection getPaths(@NotNull List virtualFiles) { return ContainerUtil.map(virtualFiles, new Function() { @Override diff --git a/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java b/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java index a6f8584486cc..fae890883a1d 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java +++ b/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java @@ -21,7 +21,7 @@ import com.intellij.notification.NotificationType; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; -import git4idea.Notificator; +import com.intellij.openapi.vcs.VcsNotifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,9 +29,6 @@ import java.awt.*; import static org.jetbrains.plugins.github.util.GithubUtil.getErrorTextFromException; -/** - * @author Aleksey Pivovarov - */ public class GithubNotifications { private static final Logger LOG = GithubUtil.LOG; @@ -40,31 +37,31 @@ public class GithubNotifications { public static void showInfo(@NotNull Project project, @NotNull String title, @NotNull String message) { LOG.info(title + "; " + message); Notification notification = new Notification(GITHUB_NOTIFICATION_GROUP, title, message, NotificationType.INFORMATION); - Notificator.getInstance(project).notify(notification); + VcsNotifier.getInstance(project).notify(notification); } public static void showWarning(@NotNull Project project, @NotNull String title, @NotNull String message) { LOG.info(title + "; " + message); Notification notification = new Notification(GITHUB_NOTIFICATION_GROUP, title, message, NotificationType.WARNING); - Notificator.getInstance(project).notify(notification); + VcsNotifier.getInstance(project).notify(notification); } public static void showError(@NotNull Project project, @NotNull String title, @NotNull String message) { LOG.info(title + "; " + message); Notification notification = new Notification(GITHUB_NOTIFICATION_GROUP, title, message, NotificationType.ERROR); - Notificator.getInstance(project).notify(notification); + VcsNotifier.getInstance(project).notify(notification); } public static void showError(@NotNull Project project, @NotNull String title, @NotNull String message, @NotNull String logDetails) { LOG.warn(title + "; " + message + "; " + logDetails); Notification notification = new Notification(GITHUB_NOTIFICATION_GROUP, title, message, NotificationType.ERROR); - Notificator.getInstance(project).notify(notification); + VcsNotifier.getInstance(project).notify(notification); } public static void showError(@NotNull Project project, @NotNull String title, @NotNull Exception e) { LOG.warn(title + "; ", e); Notification notification = new Notification(GITHUB_NOTIFICATION_GROUP, title, getErrorTextFromException(e), NotificationType.ERROR); - Notificator.getInstance(project).notify(notification); + VcsNotifier.getInstance(project).notify(notification); } public static void showInfoURL(@NotNull Project project, @NotNull String title, @NotNull String message, @NotNull String url) { @@ -72,7 +69,7 @@ public class GithubNotifications { Notification notification = new Notification(GITHUB_NOTIFICATION_GROUP, title, "" + message + "", NotificationType.INFORMATION, NotificationListener.URL_OPENING_LISTENER); - Notificator.getInstance(project).notify(notification); + VcsNotifier.getInstance(project).notify(notification); } public static void showWarningURL(@NotNull Project project, @@ -85,7 +82,7 @@ public class GithubNotifications { Notification notification = new Notification(GITHUB_NOTIFICATION_GROUP, title, prefix + "" + highlight + "" + postfix, NotificationType.WARNING, NotificationListener.URL_OPENING_LISTENER); - Notificator.getInstance(project).notify(notification); + VcsNotifier.getInstance(project).notify(notification); } public static void showErrorURL(@NotNull Project project, @@ -98,7 +95,7 @@ public class GithubNotifications { Notification notification = new Notification(GITHUB_NOTIFICATION_GROUP, title, prefix + "" + highlight + "" + postfix, NotificationType.ERROR, NotificationListener.URL_OPENING_LISTENER); - Notificator.getInstance(project).notify(notification); + VcsNotifier.getInstance(project).notify(notification); } public static void showInfoDialog(final @Nullable Project project, final @NotNull String title, final @NotNull String message) { diff --git a/plugins/github/test/org/jetbrains/plugins/github/test/GithubTest.java b/plugins/github/test/org/jetbrains/plugins/github/test/GithubTest.java index 1f7635d35601..53582045c929 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/test/GithubTest.java +++ b/plugins/github/test/org/jetbrains/plugins/github/test/GithubTest.java @@ -19,7 +19,9 @@ import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.TestVcsNotifier; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.PlatformTestCase; import com.intellij.testFramework.UsefulTestCase; @@ -28,7 +30,6 @@ import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; import git4idea.DialogManager; import git4idea.GitUtil; -import git4idea.Notificator; import git4idea.commands.GitHttpAuthService; import git4idea.commands.GitHttpAuthenticator; import git4idea.config.GitConfigUtil; @@ -39,7 +40,6 @@ import git4idea.repo.GitRepositoryManager; import git4idea.test.GitExecutor; import git4idea.test.GitTestUtil; import git4idea.test.TestDialogManager; -import git4idea.test.TestNotificator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.util.GithubAuthData; @@ -63,7 +63,6 @@ import static org.junit.Assume.assumeNotNull; * idea.test.github.password=mypassword // password for test user *

    * - * @author Kirill Likhodedov */ public abstract class GithubTest extends UsefulTestCase { @@ -77,7 +76,7 @@ public abstract class GithubTest extends UsefulTestCase { @NotNull private GitHttpAuthTestService myHttpAuthService; @NotNull protected TestDialogManager myDialogManager; - @NotNull protected TestNotificator myNotificator; + @NotNull protected TestVcsNotifier myVcsNotifier; @NotNull private IdeaProjectTestFixture myProjectFixture; @@ -104,7 +103,7 @@ public abstract class GithubTest extends UsefulTestCase { } protected void checkNotification(@NotNull NotificationType type, @Nullable String title, @Nullable String content) { - Notification actualNotification = myNotificator.getLastNotification(); + Notification actualNotification = myVcsNotifier.getLastNotification(); assertNotNull("No notification was shown", actualNotification); if (title != null) { @@ -213,7 +212,7 @@ public abstract class GithubTest extends UsefulTestCase { myGitHubSettings.setAuthData(myAuth, false); myDialogManager = (TestDialogManager)ServiceManager.getService(DialogManager.class); - myNotificator = (TestNotificator)ServiceManager.getService(myProject, Notificator.class); + myVcsNotifier = (TestVcsNotifier)ServiceManager.getService(myProject, VcsNotifier.class); myHttpAuthService = (GitHttpAuthTestService)ServiceManager.getService(GitHttpAuthService.class); myGitRepositoryManager = GitUtil.getRepositoryManager(myProject); @@ -240,7 +239,7 @@ public abstract class GithubTest extends UsefulTestCase { finally { myHttpAuthService.cleanup(); myDialogManager.cleanup(); - myNotificator.cleanup(); + myVcsNotifier.cleanup(); myProjectFixture.tearDown(); super.tearDown(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GrReferenceHighlighter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GrReferenceHighlighter.java index 4d23980e30ac..78ae905b5850 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GrReferenceHighlighter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GrReferenceHighlighter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -67,29 +67,26 @@ public class GrReferenceHighlighter extends TextEditorHighlightingPass { final TextAttributesKey attribute = GrHighlightUtil.getDeclarationHighlightingAttribute(variable, null); if (attribute != null) { final PsiElement nameElement = variable.getNameIdentifierGroovy(); - assert myInfos != null; - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION).range(nameElement); - HighlightInfo info = builder.needsUpdateOnTyping(false).textAttributes(attribute).create(); - if (info != null) { - myInfos.add(info); - } + addInfo(attribute, nameElement); } } } - private void visit(GrReferenceElement element) { ProgressManager.checkCanceled(); final PsiElement resolved = element.resolve(); final TextAttributesKey attribute = GrHighlightUtil.getDeclarationHighlightingAttribute(resolved, element); if (attribute != null) { final PsiElement refNameElement = GrHighlightUtil.getElementToHighlight(element); - assert myInfos != null; - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION).range(refNameElement); - HighlightInfo info = builder.needsUpdateOnTyping(false).textAttributes(attribute).create(); - if (info != null) { - myInfos.add(info); - } + addInfo(attribute, refNameElement); + } + } + + private void addInfo(TextAttributesKey attribute, PsiElement nameElement) { + assert myInfos != null; + HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION).range(nameElement).needsUpdateOnTyping(false).textAttributes(attribute).create(); + if (info != null) { + myInfos.add(info); } } }; @@ -100,10 +97,10 @@ public class GrReferenceHighlighter extends TextEditorHighlightingPass { final int size = myInfos.size(); super.visitReferenceExpression(referenceExpression); if (size == myInfos.size()) { - HighlightInfo info = GrUnresolvedAccessInspection.checkReferenceExpression(referenceExpression); - if (info != null) { + List infos = GrUnresolvedAccessInspection.checkReferenceExpression(referenceExpression); + if (infos != null) { assert myInfos != null; - myInfos.add(info); + myInfos.addAll(infos); } } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index 4f7ebf4bbcd4..a53647ea40ec 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -1493,13 +1493,13 @@ public class GroovyAnnotator extends GroovyElementVisitor { } @Override - public void visitDefaultAnnotationValue(GrDefaultAnnotationValue defaultAnnotationValue) { - final GrAnnotationMemberValue value = defaultAnnotationValue.getDefaultValue(); + public void visitAnnotationMethod(GrAnnotationMethod annotationMethod) { + super.visitAnnotationMethod(annotationMethod); + + final GrAnnotationMemberValue value = annotationMethod.getDefaultValue(); if (value == null) return; - final PsiElement parent = defaultAnnotationValue.getParent(); - assert parent instanceof GrAnnotationMethod; - final PsiType type = ((GrAnnotationMethod)parent).getReturnType(); + final PsiType type = annotationMethod.getReturnType(); Map errors = ContainerUtil.newHashMap(); CustomAnnotationChecker.checkAnnotationValueByType(errors, value, type, false); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyStaticImportMethodFix.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyStaticImportMethodFix.java index d5b5132f0b9e..8b17f9c4d047 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyStaticImportMethodFix.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyStaticImportMethodFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -31,6 +31,7 @@ import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.util.PsiFormatUtil; +import com.intellij.psi.util.PsiFormatUtilBase; import com.intellij.psi.util.proximity.PsiProximityComparator; import com.intellij.ui.components.JBList; import com.intellij.util.IncorrectOperationException; @@ -65,7 +66,7 @@ public class GroovyStaticImportMethodFix extends Intention { public String getText() { String text = "Static Import Method"; if (getCandidates().size() == 1) { - final int options = PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_CONTAINING_CLASS | PsiFormatUtil.SHOW_FQ_NAME; + final int options = PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_FQ_NAME; text += " '" + PsiFormatUtil.formatMethod(getCandidates().get(0), PsiSubstitutor.EMPTY, options, 0) + "'"; } else { @@ -87,8 +88,7 @@ public class GroovyStaticImportMethodFix extends Intention { public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { myCandidates = null; - return myMethodCall != null && - myMethodCall.getElement() != null && + return myMethodCall.getElement() != null && myMethodCall.getElement().isValid() && getMethodExpression(myMethodCall.getElement()) != null && getMethodExpression(myMethodCall.getElement()).getQualifierExpression() == null && diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessInspection.java index 7259fa7cff67..6b58f99d0ddc 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessInspection.java @@ -88,6 +88,9 @@ import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import org.jetbrains.plugins.groovy.util.LightCacheKey; import javax.swing.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; import static com.intellij.psi.PsiModifier.STATIC; @@ -173,9 +176,9 @@ public class GrUnresolvedAccessInspection extends GroovySuppressableInspectionTo } @Nullable - public static HighlightInfo checkReferenceExpression(GrReferenceExpression ref) { - HighlightInfo info = checkRefInner(ref); - addEmptyIntentionIfNeeded(info); + public static List checkReferenceExpression(GrReferenceExpression ref) { + List info = checkRefInner(ref); + addEmptyIntentionIfNeeded(ContainerUtil.getFirstItem(info)); return info; } @@ -251,7 +254,7 @@ public class GrUnresolvedAccessInspection extends GroovySuppressableInspectionTo } @Nullable - private static HighlightInfo checkRefInner(GrReferenceExpression ref) { + private static List checkRefInner(GrReferenceExpression ref) { PsiElement refNameElement = ref.getReferenceNameElement(); if (refNameElement == null) return null; @@ -263,7 +266,7 @@ public class GrUnresolvedAccessInspection extends GroovySuppressableInspectionTo if (!isStaticOk(resolveResult)) { String message = GroovyBundle.message("cannot.reference.non.static", ref.getReferenceName()); - return createAnnotationForRef(ref, inStaticContext, message); + return Collections.singletonList(createAnnotationForRef(ref, inStaticContext, message)); } return null; @@ -288,9 +291,11 @@ public class GrUnresolvedAccessInspection extends GroovySuppressableInspectionTo HighlightInfo info = createAnnotationForRef(ref, inStaticContext, GroovyBundle.message("cannot.resolve", ref.getReferenceName())); if (info == null) return null; + ArrayList result = ContainerUtil.newArrayList(); + result.add(info); HighlightDisplayKey displayKey = HighlightDisplayKey.find(SHORT_NAME); if (ref.getParent() instanceof GrMethodCall) { - registerStaticImportFix(ref, info, displayKey); + ContainerUtil.addIfNotNull(result, registerStaticImportFix(ref, displayKey)); } else { registerCreateClassByTypeFix(ref, info, displayKey); @@ -300,7 +305,7 @@ public class GrUnresolvedAccessInspection extends GroovySuppressableInspectionTo registerReferenceFixes(ref, info, inStaticContext, displayKey); UnresolvedReferenceQuickFixProvider.registerReferenceFixes(ref, new QuickFixActionRegistrarAdapter(info, displayKey)); OrderEntryFix.registerFixes(new QuickFixActionRegistrarAdapter(info, displayKey), ref); - return info; + return result; } return null; @@ -534,14 +539,16 @@ public class GrUnresolvedAccessInspection extends GroovySuppressableInspectionTo return HighlightInfo.newHighlightInfo(highlightInfoType).range(refNameElement).descriptionAndTooltip(message).create(); } - private static void registerStaticImportFix(@NotNull GrReferenceExpression referenceExpression, - @Nullable HighlightInfo info, - @Nullable final HighlightDisplayKey key) { + private static HighlightInfo registerStaticImportFix(@NotNull GrReferenceExpression referenceExpression, + @Nullable final HighlightDisplayKey key) { final String referenceName = referenceExpression.getReferenceName(); - if (StringUtil.isEmpty(referenceName)) return; - if (referenceExpression.getQualifier() != null) return; + if (StringUtil.isEmpty(referenceName)) return null; + if (referenceExpression.getQualifier() != null) return null; + HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION).range( + referenceExpression.getParent()).createUnconditionally(); QuickFixAction.registerQuickFixAction(info, new GroovyStaticImportMethodFix((GrMethodCall)referenceExpression.getParent()), key); + return info; } private static void registerReferenceFixes(GrReferenceExpression refExpr, diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManagerFactory.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManagerFactory.java index 12aaf2a53bad..200390323ba9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManagerFactory.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManagerFactory.java @@ -18,13 +18,14 @@ package org.jetbrains.plugins.groovy.debugger; import com.intellij.debugger.PositionManager; import com.intellij.debugger.PositionManagerFactory; import com.intellij.debugger.engine.DebugProcess; +import org.jetbrains.annotations.NotNull; /** * @author peter */ public class GroovyPositionManagerFactory extends PositionManagerFactory { @Override - public PositionManager createPositionManager(DebugProcess process) { + public PositionManager createPositionManager(@NotNull DebugProcess process) { return new GroovyPositionManager(process); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovyIndentProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovyIndentProcessor.java index c81df59cd7fa..b004679e8fb9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovyIndentProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovyIndentProcessor.java @@ -36,10 +36,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrThrowsClause; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArgumentList; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArrayInitializer; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.*; import org.jetbrains.plugins.groovy.lang.psi.api.statements.*; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; @@ -55,6 +52,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrExtendsCla import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinitionBody; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAnnotationMethod; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeArgumentList; @@ -407,8 +405,13 @@ public class GroovyIndentProcessor extends GroovyElementVisitor { } @Override - public void visitDefaultAnnotationValue(GrDefaultAnnotationValue defaultAnnotationValue) { - myResult = Indent.getContinuationIndent(); + public void visitAnnotationMethod(GrAnnotationMethod annotationMethod) { + if (myChild instanceof GrAnnotationMemberValue) { + myResult = Indent.getContinuationIndent(); + } + else { + super.visitAnnotationMethod(annotationMethod); + } } @Override diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java index e64154c3a14b..fd2e9bd42516 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java @@ -69,6 +69,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import static org.jetbrains.plugins.groovy.GroovyFileType.GROOVY_LANGUAGE; import static org.jetbrains.plugins.groovy.formatter.models.spacing.SpacingTokens.*; import static org.jetbrains.plugins.groovy.formatter.models.spacing.SpacingTokens.GROOVY_DOC_COMMENT; +import static org.jetbrains.plugins.groovy.formatter.models.spacing.SpacingTokens.kDEFAULT; import static org.jetbrains.plugins.groovy.formatter.models.spacing.SpacingTokens.kIN; import static org.jetbrains.plugins.groovy.formatter.models.spacing.SpacingTokens.mCOMMA; import static org.jetbrains.plugins.groovy.formatter.models.spacing.SpacingTokens.mELVIS; @@ -684,7 +685,7 @@ public class GroovySpacingProcessor extends GroovyElementVisitor { @Override public void visitAnnotationMethod(GrAnnotationMethod annotationMethod) { - if (myType2 == DEFAULT_ANNOTATION_VALUE) { + if (myType2 == kDEFAULT) { createSpaceInCode(true); } else { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/ImportStaticIntention.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/ImportStaticIntention.java index c5bea4a6709a..7bccfea8a963 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/ImportStaticIntention.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/ImportStaticIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -23,13 +23,17 @@ import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.codeStyle.GrReferenceAdjuster; import org.jetbrains.plugins.groovy.intentions.base.Intention; import org.jetbrains.plugins.groovy.intentions.base.PsiElementPredicate; import org.jetbrains.plugins.groovy.lang.psi.GrQualifiedReference; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeArgumentList; @@ -41,27 +45,19 @@ public class ImportStaticIntention extends Intention { private static final Key TEMP_REFERENT_USER_DATA = new Key("TEMP_REFERENT_USER_DATA"); @Override - protected void processIntention(@NotNull PsiElement element, final Project project, final Editor editor) - throws IncorrectOperationException { - final PsiElement resolved; - final String name; - final GroovyFile file; - final GrImportStatement importStatement; - boolean isAnythingShortened; - if (!(element instanceof GrReferenceExpression)) return; - final GrReferenceExpression ref = (GrReferenceExpression)element; - resolved = ref.resolve(); + protected void processIntention(@NotNull PsiElement element, final Project project, final Editor editor) throws IncorrectOperationException { + final PsiElement resolved = resolve(element); if (!(resolved instanceof PsiMember)) return; final PsiClass containingClass = ((PsiMember)resolved).getContainingClass(); if (containingClass == null) return; final String qname = containingClass.getQualifiedName(); - name = ((PsiMember)resolved).getName(); + final String name = ((PsiMember)resolved).getName(); if (name == null) return; final PsiFile containingFile = element.getContainingFile(); if (!(containingFile instanceof GroovyFile)) return; - file = (GroovyFile)containingFile; + final GroovyFile file = (GroovyFile)containingFile; file.accept(new GroovyRecursiveElementVisitor() { @Override public void visitReferenceExpression(GrReferenceExpression expression) { @@ -77,17 +73,9 @@ public class ImportStaticIntention extends Intention { final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project); final GrImportStatement tempImport = factory.createImportStatementFromText(qname + "." + name, true, false, null); - importStatement = file.addImport(tempImport); - - isAnythingShortened = false; - for (PsiReference reference : ReferencesSearch.search(resolved, new LocalSearchScope(containingFile))) { - final PsiElement refElement = reference.getElement(); - if (refElement instanceof GrQualifiedReference) { - isAnythingShortened |= - org.jetbrains.plugins.groovy.codeStyle.GrReferenceAdjuster.shortenReference((GrQualifiedReference)refElement); - } - } + final GrImportStatement importStatement = file.addImport(tempImport); + boolean isAnythingShortened = shortenUsages(resolved, containingFile); if (!isAnythingShortened) { importStatement.delete(); return; @@ -111,7 +99,7 @@ public class ImportStaticIntention extends Intention { if (qualifier instanceof GrReferenceExpression) { PsiElement aClass = ((GrReferenceExpression)qualifier).resolve(); if (aClass == ((PsiMember)resolved).getContainingClass()) { - org.jetbrains.plugins.groovy.codeStyle.GrReferenceAdjuster.shortenReference(expression); + GrReferenceAdjuster.shortenReference(expression); } } } @@ -129,6 +117,18 @@ public class ImportStaticIntention extends Intention { }); } + private static boolean shortenUsages(PsiElement resolved, PsiFile containingFile) { + boolean isAnythingShortened = false; + for (PsiReference reference : ReferencesSearch.search(resolved, new LocalSearchScope(containingFile))) { + final PsiElement refElement = reference.getElement(); + if (refElement instanceof GrQualifiedReference) { + boolean shortened = GrReferenceAdjuster.shortenReference((GrQualifiedReference)refElement); + isAnythingShortened |= shortened; + } + } + return isAnythingShortened; + } + @Override protected boolean isStopElement(PsiElement element) { return super.isStopElement(element) || element instanceof GrReferenceExpression; @@ -140,15 +140,39 @@ public class ImportStaticIntention extends Intention { return new PsiElementPredicate() { @Override public boolean satisfiedBy(PsiElement element) { - if (!(element instanceof GrReferenceExpression)) return false; - final GrReferenceExpression ref = (GrReferenceExpression)element; - if (ref.getQualifier() == null) return false; - final PsiElement resolved = ref.resolve(); - if (resolved == null) return false; - return resolved instanceof PsiMember && !(resolved instanceof PsiClass) && - ((PsiMember)resolved).hasModifierProperty(PsiModifier.STATIC) && - ((PsiMember)resolved).getContainingClass() != null; + final PsiElement resolved = resolve(element); + + if (resolved == null) return false; + return resolved instanceof PsiMember && !(resolved instanceof PsiClass) && + ((PsiMember)resolved).hasModifierProperty(PsiModifier.STATIC) && + ((PsiMember)resolved).getContainingClass() != null; } }; } + + @Nullable + private static PsiElement resolve(PsiElement element) { + GrReferenceExpression ref = findRef(element); + + if (ref == null || ref.getQualifier() == null) return null; + return ref.resolve(); + } + + @Nullable + private static GrReferenceExpression findRef(PsiElement element) { + if ((element instanceof GrReferenceExpression)) { + return (GrReferenceExpression)element; + + } + else if (element instanceof GrArgumentList) { + PsiElement parent = element.getParent(); + if (parent instanceof GrMethodCall) { + GrExpression invoked = ((GrMethodCall)parent).getInvokedExpression(); + if (invoked instanceof GrReferenceExpression) { + return ((GrReferenceExpression)invoked); + } + } + } + return null; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyAnnotationAttributeInfoHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyAnnotationAttributeInfoHandler.java index 8d6705087992..35030c19110a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyAnnotationAttributeInfoHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyAnnotationAttributeInfoHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -18,7 +18,6 @@ package org.jetbrains.plugins.groovy.lang.parameterInfo; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.lang.parameterInfo.*; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; @@ -197,6 +196,6 @@ public class GroovyAnnotationAttributeInfoHandler implements ParameterInfoHandle } - context.setupUIComponentPresentation(StringUtil.escapeXml(buffer.toString()), highlightStartOffset, highlightEndOffset, false, p.isDeprecated(), false, context.getDefaultParameterColor()); + context.setupUIComponentPresentation(buffer.toString(), highlightStartOffset, highlightEndOffset, false, p.isDeprecated(), false, context.getDefaultParameterColor()); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyTypeParameterInfoHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyTypeParameterInfoHandler.java index 4924d0175b33..2e54bef62d8e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyTypeParameterInfoHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyTypeParameterInfoHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -34,7 +34,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; -import java.util.Arrays; import java.util.Set; /** @@ -156,13 +155,13 @@ public class GroovyTypeParameterInfoHandler implements ParameterInfoHandlerWithT buffer.append(p.getName()); int highlightEndOffset = buffer.length(); buffer.append(" extends "); - buffer.append(StringUtil.join(Arrays.asList(p.getSuperTypes()), new Function() { - @Override - public String fun(final PsiClassType t) { - return t.getPresentableText(); - } - }, ", ")); + buffer.append(StringUtil.join(p.getSuperTypes(), new Function() { + @Override + public String fun(final PsiClassType t) { + return t.getPresentableText(); + } + }, ", ")); - context.setupUIComponentPresentation(StringUtil.escapeXml(buffer.toString()), 0, highlightEndOffset, false, false, false, context.getDefaultParameterColor()); + context.setupUIComponentPresentation(buffer.toString(), 0, highlightEndOffset, false, false, false, context.getDefaultParameterColor()); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java index a071bd23a3f5..a7f0524baddd 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -104,8 +104,6 @@ public class GroovyPsiCreator implements GroovyElementTypes { if (elem == ANNOTATION_ARRAY_INITIALIZER) return new GrAnnotationArrayInitializerImpl(node); if (elem == ANNOTATION_MEMBER_VALUE_PAIR) return new GrAnnotationNameValuePairImpl(node); - if (elem == DEFAULT_ANNOTATION_VALUE) return new GrDefaultAnnotationValueImpl(node); - //throws if (elem == THROW_CLAUSE) return new GrThrowsClauseImpl(node); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/declaration/VariableDefinitions.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/declaration/VariableDefinitions.java index 80d0ee6c32a7..fa4db282a732 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/declaration/VariableDefinitions.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/declaration/VariableDefinitions.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * 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. @@ -152,15 +152,12 @@ public class VariableDefinitions implements GroovyElementTypes { } if (isAnnotationMember && builder.getTokenType() == kDEFAULT) { - PsiBuilder.Marker defaultValueMarker = builder.mark(); ParserUtils.getToken(builder, kDEFAULT); ParserUtils.getToken(builder, mNLS); if (!AnnotationArguments.parseAnnotationMemberValueInitializer(builder, parser)) { builder.error(GroovyBundle.message("annotation.initializer.expected")); } - - defaultValueMarker.done(DEFAULT_ANNOTATION_VALUE); } if (ParserUtils.lookAhead(builder, mNLS, kTHROWS) || ParserUtils.lookAhead(builder, mNLS, mLCURLY)) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java index c548b56eafdd..bf65baa23c6e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java @@ -296,10 +296,6 @@ public abstract class GroovyElementVisitor { visitMethod(annotationMethod); } - public void visitDefaultAnnotationValue(GrDefaultAnnotationValue defaultAnnotationValue) { - visitElement(defaultAnnotationValue); - } - public void visitMethod(GrMethod method) { visitElement(method); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrAnnotationMethod.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrAnnotationMethod.java index be21a511ea31..255abd0339e3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrAnnotationMethod.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrAnnotationMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -17,13 +17,14 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members; import com.intellij.psi.PsiAnnotationMethod; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrDefaultAnnotationValue; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationMemberValue; /** * User: Dmitry.Krasilschikov * Date: 04.06.2007 */ public interface GrAnnotationMethod extends GrMethod, PsiAnnotationMethod { + @Nullable - GrDefaultAnnotationValue getDefaultValue(); + GrAnnotationMemberValue getDefaultValue(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java index 1d541a0b83c2..bc48bb2a043a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java @@ -205,9 +205,7 @@ public class GroovyExpectedTypesProvider { } @Override - public void visitDefaultAnnotationValue(GrDefaultAnnotationValue defaultAnnotationValue) { - final GrAnnotationMethod method = ((GrAnnotationMethod)defaultAnnotationValue.getParent()); - + public void visitAnnotationMethod(GrAnnotationMethod method) { PsiType type = method.getReturnType(); if (type != null && isAcceptableAnnotationValueType(type)) { myResult = createSimpleSubTypeResult(type); @@ -216,7 +214,7 @@ public class GroovyExpectedTypesProvider { @Override public void visitAnnotationArrayInitializer(GrAnnotationArrayInitializer arrayInitializer) { - final GrAnnotationNameValuePair nameValuePair = PsiTreeUtil.getParentOfType(arrayInitializer, GrAnnotationNameValuePair.class, true, GrDefaultAnnotationValue.class); + final GrAnnotationNameValuePair nameValuePair = PsiTreeUtil.getParentOfType(arrayInitializer, GrAnnotationNameValuePair.class, true, GrAnnotationMethod.class); if (nameValuePair != null) { final PsiClass annot = ResolveUtil.resolveAnnotation(arrayInitializer); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrDefaultAnnotationValueImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrDefaultAnnotationValueImpl.java deleted file mode 100644 index cb878623a383..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrDefaultAnnotationValueImpl.java +++ /dev/null @@ -1,47 +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 org.jetbrains.plugins.groovy.lang.psi.impl.statements; - -import com.intellij.lang.ASTNode; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationMemberValue; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrDefaultAnnotationValue; -import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiElementImpl; - -/** - * User: Dmitry.Krasilschikov - * Date: 04.06.2007 - */ -public class GrDefaultAnnotationValueImpl extends GroovyPsiElementImpl implements GrDefaultAnnotationValue { - - public GrDefaultAnnotationValueImpl(@NotNull ASTNode node) { - super(node); - } - - public void accept(GroovyElementVisitor visitor) { - visitor.visitDefaultAnnotationValue(this); - } - - public String toString() { - return "Default annotation value"; - } - - @Override - public GrAnnotationMemberValue getDefaultValue() { - return findChildByClass(GrAnnotationMemberValue.class); - } -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java index 6e312234decb..e1ef4491d27b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java @@ -242,6 +242,11 @@ public class GrVariableDeclarationImpl extends GrStubElementBase impl public Object[] getVariants() { return EMPTY_ARRAY; } + + @Override + public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { + return getElement(); + } }; } else { @@ -257,6 +262,11 @@ public class GrVariableDeclarationImpl extends GrStubElementBase impl public Object[] getVariants() { return EMPTY_ARRAY; } + + @Override + public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { + return getElement(); + } }; } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/members/GrAnnotationMethodImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/members/GrAnnotationMethodImpl.java index 2286e4a59b6b..69f345555c84 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/members/GrAnnotationMethodImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/members/GrAnnotationMethodImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -20,7 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrDefaultAnnotationValue; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationMemberValue; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAnnotationMethod; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrMethodStub; @@ -54,7 +54,7 @@ public class GrAnnotationMethodImpl extends GrMethodBaseImpl implements GrAnnota } @Override - public GrDefaultAnnotationValue getDefaultValue() { - return findChildByClass(GrDefaultAnnotationValue.class); + public GrAnnotationMemberValue getDefaultValue() { + return findChildByClass(GrAnnotationMemberValue.class); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/toplevel/imports/GrImportStatementImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/toplevel/imports/GrImportStatementImpl.java index 761cbeb31b6c..03e8016f08bf 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/toplevel/imports/GrImportStatementImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/toplevel/imports/GrImportStatementImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -25,7 +25,6 @@ import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiModificationTracker; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -78,7 +77,7 @@ public class GrImportStatementImpl extends GrStubElementBase) +''') + + myFixture.getAvailableIntention("Static Import Method 'A.foo'") + } } \ No newline at end of file diff --git a/plugins/groovy/testdata/parsing/groovy/types/ann_def3.test b/plugins/groovy/testdata/parsing/groovy/types/ann_def3.test index 026b3d40209c..4c35706139e2 100644 --- a/plugins/groovy/testdata/parsing/groovy/types/ann_def3.test +++ b/plugins/groovy/testdata/parsing/groovy/types/ann_def3.test @@ -31,14 +31,13 @@ Groovy script PsiElement())(')') PsiWhiteSpace(' ') - Default annotation value - PsiElement(default)('default') - PsiWhiteSpace(' ') - Annotation array initializer - PsiElement([)('[') - Literal - PsiElement(Integer)('8') - PsiElement(])(']') + PsiElement(default)('default') + PsiWhiteSpace(' ') + Annotation array initializer + PsiElement([)('[') + Literal + PsiElement(Integer)('8') + PsiElement(])(']') Throw clause PsiElement(;)(';') diff --git a/plugins/groovy/testdata/parsing/groovy/types/default1.test b/plugins/groovy/testdata/parsing/groovy/types/default1.test index 94cc6ece5f07..46d59f6653be 100644 --- a/plugins/groovy/testdata/parsing/groovy/types/default1.test +++ b/plugins/groovy/testdata/parsing/groovy/types/default1.test @@ -34,15 +34,14 @@ Groovy script PsiElement())(')') PsiWhiteSpace(' ') - Default annotation value - PsiElement(default)('default') - PsiWhiteSpace(' ') - Annotation - PsiElement(@)('@') - Reference element - PsiElement(identifier)('Bar') - Annotation arguments - + PsiElement(default)('default') + PsiWhiteSpace(' ') + Annotation + PsiElement(@)('@') + Reference element + PsiElement(identifier)('Bar') + Annotation arguments + Throw clause PsiWhiteSpace(' ') diff --git a/plugins/groovy/testdata/parsing/groovy/types/default2.test b/plugins/groovy/testdata/parsing/groovy/types/default2.test index 05a31b6762d9..026bb8c470df 100644 --- a/plugins/groovy/testdata/parsing/groovy/types/default2.test +++ b/plugins/groovy/testdata/parsing/groovy/types/default2.test @@ -34,11 +34,10 @@ Groovy script PsiElement())(')') PsiWhiteSpace(' ') - Default annotation value - PsiElement(default)('default') - PsiWhiteSpace(' ') - Literal - PsiElement(Integer)('5') + PsiElement(default)('default') + PsiWhiteSpace(' ') + Literal + PsiElement(Integer)('5') Throw clause PsiElement(new line)('\n') diff --git a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties index 5f5a1679709f..af459be969f5 100644 --- a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties +++ b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties @@ -48,7 +48,7 @@ hg4idea.clone.test.success=Connection Successful hg4idea.configuration.title=Mercurial Configuration hg4idea.configuration.executable.title=Mercurial executable hg4idea.configuration.description=Specify the full path to hg executable -hg4idea.configuration.executable.error=\"{0}\" doesn''t appear to be a valid hg executable +hg4idea.configuration.executable.error=\"{0}\" doesn't appear to be a valid hg executable hg4idea.configuration.test.label=Test hg4idea.configuration.test=&Test hg4idea.unable.to.run.hg=Unable to run hg: {0} @@ -80,7 +80,7 @@ hg4idea.init.already.under.hg.option.create.repo.here=Create new repository here hg4idea.init.created.notification.title=Mercurial repository created hg4idea.init.created.notification.description=Repository was created in {0} hg4idea.init.error.title=Error creating Mercurial repository -hg4idea.init.error.description=Couldn''t create a Mercurial repository in {0} +hg4idea.init.error.description=Couldn't create a Mercurial repository in {0} hg4idea.move.progress=Moving files in the VCS... diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java index 17d4f364a154..2e9321253b7e 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java @@ -21,6 +21,7 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -90,12 +91,10 @@ public class HgPusher { String successTitle = "Pushed successfully"; String successDescription = String.format("Pushed %d %s [%s]", commitsNum, StringUtil.pluralize("commit", commitsNum), repo.getPresentableName()); - new HgCommandResultNotifier(project).notifySuccess(successTitle, successDescription); - } - else if (result.getExitValue() == NOTHING_TO_PUSH_EXIT_VALUE) { - new HgCommandResultNotifier(project).notifySuccess("", "Nothing to push"); - } - else { + VcsNotifier.getInstance(project).notifySuccess(successTitle, successDescription); + } else if (result.getExitValue() == NOTHING_TO_PUSH_EXIT_VALUE) { + VcsNotifier.getInstance(project).notifySuccess("Nothing to push"); + } else { new HgCommandResultNotifier(project).notifyError(result, "Push failed", "Failed to push to [" + repo.getPresentableName() + "]"); } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java index 0c6ac2aa227d..8745a6b738b5 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java @@ -18,6 +18,7 @@ import com.intellij.notification.NotificationListener; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.patch.formove.FilePathComparator; import com.intellij.openapi.editor.markup.TextAttributes; @@ -38,6 +39,7 @@ import com.intellij.openapi.vcs.diff.DiffProvider; import com.intellij.openapi.vcs.history.VcsHistoryProvider; import com.intellij.openapi.vcs.merge.MergeProvider; import com.intellij.openapi.vcs.rollback.RollbackEnvironment; +import com.intellij.openapi.vcs.roots.VcsRootDetector; import com.intellij.openapi.vcs.update.UpdateEnvironment; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vfs.VfsUtilCore; @@ -48,12 +50,12 @@ import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.messages.Topic; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.zmlx.hg4idea.action.HgCommandResultNotifier; import org.zmlx.hg4idea.provider.*; import org.zmlx.hg4idea.provider.annotate.HgAnnotationProvider; import org.zmlx.hg4idea.provider.commit.HgCheckinEnvironment; import org.zmlx.hg4idea.provider.commit.HgCommitAndPushExecutor; import org.zmlx.hg4idea.provider.update.HgUpdateEnvironment; +import org.zmlx.hg4idea.roots.HgIntegrationEnabler; import org.zmlx.hg4idea.status.HgRemoteStatusUpdater; import org.zmlx.hg4idea.status.ui.HgHideableWidget; import org.zmlx.hg4idea.status.ui.HgIncomingOutgoingWidget; @@ -63,6 +65,7 @@ import org.zmlx.hg4idea.util.HgVersion; import javax.swing.event.HyperlinkEvent; import java.io.File; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -401,6 +404,17 @@ public class HgVcs extends AbstractVcs { return VcsType.distributed; } + @Override + @CalledInAwt + public void enableIntegration() { + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public void run() { + Collection roots = ServiceManager.getService(myProject, VcsRootDetector.class).detect(); + new HgIntegrationEnabler(HgVcs.this).enable(roots); + } + }); + } + @Override public CheckoutProvider getCheckoutProvider() { return new HgCheckoutProvider(); @@ -412,7 +426,7 @@ public class HgVcs extends AbstractVcs { */ public void checkVersion() { final String executable = getGlobalSettings().getHgExecutable(); - HgCommandResultNotifier errorNotification = new HgCommandResultNotifier(myProject); + VcsNotifier vcsNotifier = VcsNotifier.getInstance(myProject); final String SETTINGS_LINK = "settings"; final String UPDATE_LINK = "update"; NotificationListener linkAdapter = new NotificationListener.Adapter() { @@ -436,14 +450,14 @@ public class HgVcs extends AbstractVcs { String message = String.format("The configured version of Hg is not supported: %s.
    " + "The minimal supported version is %s. Please update.", myVersion, HgVersion.MIN); - errorNotification.notifyError(null, "Unsupported Hg version", message, linkAdapter); + vcsNotifier.notifyError("Unsupported Hg version", message, linkAdapter); } else if (myVersion.hasUnsupportedExtensions()) { String unsupportedExtensionsAsString = myVersion.getUnsupportedExtensions().toString(); LOG.warn("Unsupported Hg extensions: " + unsupportedExtensionsAsString); String message = String.format("Some hg extensions %s are not found or not supported by your hg version and will be ignored.\n" + "Please, update your hgrc or Mercurial.ini file", unsupportedExtensionsAsString); - errorNotification.notifyWarning("Unsupported Hg version", message); + vcsNotifier.notifyWarning("Unsupported Hg version", message); } } catch (Exception e) { @@ -452,13 +466,15 @@ public class HgVcs extends AbstractVcs { // so parse(output) throw ParseException, but hg and git executable seems to be valid in this case final String reason = (e.getCause() != null ? e.getCause() : e).getMessage(); String message = HgVcsMessages.message("hg4idea.unable.to.run.hg", executable); - errorNotification.notifyError(null, message, - String.format( - reason + - "
    Please check your hg executable path in settings "), - linkAdapter); + vcsNotifier.notifyError(message, + String.format( + reason + + "
    Please check your hg executable path in settings " + ), + linkAdapter + ); } } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommandResultNotifier.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommandResultNotifier.java index 73d5506396c7..45cfd6cfb6d1 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommandResultNotifier.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommandResultNotifier.java @@ -12,14 +12,11 @@ // limitations under the License. package org.zmlx.hg4idea.action; -import com.intellij.notification.NotificationDisplayType; -import com.intellij.notification.NotificationGroup; import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,23 +28,11 @@ public final class HgCommandResultNotifier { private final Project myProject; private static final Logger LOG = Logger.getInstance(HgCommandResultNotifier.class); - public static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.toolWindowGroup( - "Mercurial Messages", ChangesViewContentManager.TOOLWINDOW_ID, true); - public static final NotificationGroup IMPORTANT_ERROR_NOTIFICATION = new NotificationGroup( - "Mercurial Important Messages", NotificationDisplayType.STICKY_BALLOON, true); public HgCommandResultNotifier(Project project) { myProject = project; } - public void notifySuccess(@NotNull String title, @NotNull String successDescription) { - NOTIFICATION_GROUP.createNotification(title, successDescription, NotificationType.INFORMATION, null).notify(myProject); - } - - public void notifyWarning(@NotNull String title, @NotNull String warningDescription) { - NOTIFICATION_GROUP.createNotification(title, warningDescription, NotificationType.WARNING, null).notify(myProject); - } - public void notifyError(@Nullable HgCommandResult result, @NotNull String failureTitle, @NotNull String failureDescription) { notifyError(result, failureTitle, failureDescription, null); } @@ -75,8 +60,6 @@ public final class HgCommandResultNotifier { errorMessage = XmlStringUtil.wrapInHtml(failureDescription + "
    " + StringUtil.join(err, "
    ")); } } - IMPORTANT_ERROR_NOTIFICATION - .createNotification(failureTitle, errorMessage, NotificationType.ERROR, listener) - .notify(myProject); + VcsNotifier.getInstance(myProject).notifyError(failureTitle, errorMessage, listener); } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommonBranchActions.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommonBranchActions.java index 992a26c6ab5e..a8972f86939a 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommonBranchActions.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommonBranchActions.java @@ -22,6 +22,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -73,7 +74,6 @@ public class HgCommonBranchActions extends ActionGroup { final HgMergeCommand hgMergeCommand = new HgMergeCommand(myProject, mySelectedRepository.getRoot()); hgMergeCommand.setRevision(myBranchName);//there is no difference between branch or revision or bookmark as parameter to merge, // we need just a string - final HgCommandResultNotifier notifier = new HgCommandResultNotifier(myProject); new Task.Backgroundable(myProject, "Merging changes...") { @Override public void run(@NotNull ProgressIndicator indicator) { @@ -84,11 +84,12 @@ public class HgCommonBranchActions extends ActionGroup { } catch (VcsException exception) { + assert myProject != null; // myProject couldn't be null, see annotation for Merge action if (exception.isWarning()) { - notifier.notifyWarning("Warning during merge", exception.getMessage()); + VcsNotifier.getInstance(myProject).notifyWarning("Warning during merge", exception.getMessage()); } else { - notifier.notifyError(null, "Exception during merge", exception.getMessage()); + VcsNotifier.getInstance(myProject).notifyError("Exception during merge", exception.getMessage()); } } catch (Exception e1) { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgInit.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgInit.java index 27346660015b..7d329a3f3652 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgInit.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgInit.java @@ -2,12 +2,12 @@ package org.zmlx.hg4idea.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsDirectoryMapping; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.Nullable; @@ -116,15 +116,17 @@ public class HgInit extends DumbAwareAction { public void process(@Nullable HgCommandResult result) { if (!HgErrorUtil.hasErrorsInCommandExecution(result)) { updateDirectoryMappings(mapRoot); - new HgCommandResultNotifier(myProject.isDefault() ? null : myProject) - .notifySuccess(HgVcsMessages.message("hg4idea.init.created.notification.title"), - HgVcsMessages.message("hg4idea.init.created.notification.description", selectedRoot.getPresentableUrl())); + VcsNotifier.getInstance(myProject).notifySuccess(HgVcsMessages.message("hg4idea.init.created.notification.title"), + HgVcsMessages.message("hg4idea.init.created.notification.description", + selectedRoot.getPresentableUrl()) + ); } else { new HgCommandResultNotifier(myProject.isDefault() ? null : myProject) .notifyError(result, HgVcsMessages.message("hg4idea.init.error.title"), HgVcsMessages.message("hg4idea.init.error.description", selectedRoot - .getPresentableUrl())); + .getPresentableUrl() + )); } } }); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgMerge.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgMerge.java index 87c907d76d59..1743e7e5b56e 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgMerge.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgMerge.java @@ -19,6 +19,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -62,7 +63,6 @@ public class HgMerge extends HgAbstractGlobalAction { private static void executeMerge(@NotNull final Project project, @NotNull VirtualFile repo, @NotNull String targetValue) throws HgCommandException { UpdatedFiles updatedFiles = UpdatedFiles.create(); - HgCommandResultNotifier notifier = new HgCommandResultNotifier(project); HgMergeCommand hgMergeCommand = new HgMergeCommand(project, repo); hgMergeCommand.setRevision(targetValue); @@ -73,10 +73,10 @@ public class HgMerge extends HgAbstractGlobalAction { } catch (VcsException e) { if (e.isWarning()) { - notifier.notifyWarning("Warning during merge", e.getMessage()); + VcsNotifier.getInstance(project).notifyWarning("Warning during merge", e.getMessage()); } else { - notifier.notifyError(null, "Exception during merge", e.getMessage()); + VcsNotifier.getInstance(project).notifyError("Exception during merge", e.getMessage()); } } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java index 9c90f5fc11f5..52ed34f39f17 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java @@ -68,7 +68,7 @@ public class HgLogProvider implements VcsLogProvider { @NotNull @Override public List readAllHashes(@NotNull VirtualFile root, @NotNull Consumer userRegistry) throws VcsException { - return HgHistoryUtil.readAllHashes(myProject, root, userRegistry); + return HgHistoryUtil.readAllHashes(myProject, root, userRegistry, Collections.emptyList()); } @NotNull @@ -155,9 +155,9 @@ public class HgLogProvider implements VcsLogProvider { @NotNull @Override - public List getFilteredDetails(@NotNull final VirtualFile root, - @NotNull VcsLogFilterCollection filterCollection, - int maxCount) throws VcsException { + public List getCommitsMatchingFilter(@NotNull final VirtualFile root, + @NotNull VcsLogFilterCollection filterCollection, + int maxCount) throws VcsException { List filterParameters = ContainerUtil.newArrayList(); // branch filter and user filter may be used several times without delimiter @@ -216,7 +216,7 @@ public class HgLogProvider implements VcsLogProvider { } } - return HgHistoryUtil.history(myProject, root, maxCount, filterParameters); + return HgHistoryUtil.readAllHashes(myProject, root, Consumer.EMPTY_CONSUMER, filterParameters); } @Nullable diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/roots/HgIntegrationEnabler.java b/plugins/hg4idea/src/org/zmlx/hg4idea/roots/HgIntegrationEnabler.java new file mode 100644 index 000000000000..853c4d00ddc6 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/roots/HgIntegrationEnabler.java @@ -0,0 +1,60 @@ +/* + * 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 org.zmlx.hg4idea.roots; + +import com.intellij.openapi.vcs.VcsNotifier; +import com.intellij.openapi.vcs.roots.VcsIntegrationEnabler; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.HgVcs; +import org.zmlx.hg4idea.HgVcsMessages; +import org.zmlx.hg4idea.command.HgInitCommand; +import org.zmlx.hg4idea.execution.HgCommandResult; +import org.zmlx.hg4idea.execution.HgCommandResultHandler; +import org.zmlx.hg4idea.util.HgErrorUtil; + +public class HgIntegrationEnabler extends VcsIntegrationEnabler { + + public HgIntegrationEnabler(@NotNull HgVcs vcs) { + super(vcs); + } + + @Override + protected boolean initOrNotifyError(@NotNull final VirtualFile projectDir) { + final boolean[] success = new boolean[1]; + new HgInitCommand(myProject).execute(projectDir, new HgCommandResultHandler() { + @Override + public void process(@Nullable HgCommandResult result) { + VcsNotifier notification = VcsNotifier.getInstance(myProject); + if (!HgErrorUtil.hasErrorsInCommandExecution(result)) { + success[0] = true; + notification.notifySuccess(HgVcsMessages.message("hg4idea.init.created.notification.title"), + HgVcsMessages + .message("hg4idea.init.created.notification.description", projectDir.getPresentableUrl()) + ); + } + else { + success[0] = false; + String errors = result != null ? result.getRawError() : ""; + notification.notifyError( + HgVcsMessages.message("hg4idea.init.error.description", projectDir.getPresentableUrl()), errors); + } + } + }); + return success[0]; + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgHistoryUtil.java b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgHistoryUtil.java index 2847323b2174..ba72f36f50c0 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgHistoryUtil.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgHistoryUtil.java @@ -142,10 +142,10 @@ public class HgHistoryUtil { @NotNull public static List readAllHashes(@NotNull Project project, @NotNull VirtualFile root, - @NotNull final Consumer userRegistry) throws VcsException { + @NotNull final Consumer userRegistry, List params) throws VcsException { final VcsLogObjectsFactory factory = ServiceManager.getService(project, VcsLogObjectsFactory.class); - return ContainerUtil.map(getCommittedChangeList(project, root, -1, false, Collections.emptyList()), new Function() { + return ContainerUtil.map(getCommittedChangeList(project, root, -1, false, params), new Function() { @Override public TimedVcsCommit fun(HgCommittedChangeList record) { HgRevisionNumber revNumber = (HgRevisionNumber)record.getRevisionNumber(); diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/JavaFxFileReferenceProvider.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/JavaFxFileReferenceProvider.java index b419e2bfd68f..9a27f6e904c7 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/JavaFxFileReferenceProvider.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/JavaFxFileReferenceProvider.java @@ -36,7 +36,7 @@ public class JavaFxFileReferenceProvider extends PsiReferenceProvider { return value; } - public static PsiReference[] getReferences(final PsiElement element, String value, final String acceptedExtension) { + public static PsiReference[] getReferences(@NotNull PsiElement element, String value, final String acceptedExtension) { final PsiDirectory directory = element.getContainingFile().getOriginalFile().getParent(); if (directory == null) return PsiReference.EMPTY_ARRAY; final boolean startsWithSlash = value.startsWith("/"); diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxLocationReferenceProvider.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxLocationReferenceProvider.java index a6f84fed2d47..ddfba6a6aaa9 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxLocationReferenceProvider.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxLocationReferenceProvider.java @@ -78,7 +78,7 @@ class JavaFxLocationReferenceProvider extends PsiReferenceProvider { } } - private static PsiReference[] collectRefs(PsiElement element, String value, final int startInElement, final FileType... acceptedFileTypes) { + private static PsiReference[] collectRefs(@NotNull PsiElement element, String value, final int startInElement, final FileType... acceptedFileTypes) { final FileReferenceSet set = new FileReferenceSet(value, element, startInElement, null, true){ @Override protected Condition getReferenceCompletionFilter() { diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java index 1a27f94ff708..db4c1a213eae 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java @@ -998,4 +998,31 @@ public abstract class ResourceFilteringTest extends MavenCompilingTestCase { assertNotNull(myProjectPom.getParent().findFileByRelativePath("target/classes/file.xyz")); } + + public void testTwoResourcesMapping() throws Exception { + createProjectSubFile("resources/file.properties", "value=${project.version}\n"); + + importProject("test" + + "project" + + "1" + + "" + + + "" + + " " + + " " + + " resources" + + " false" + + " " + + " " + + " resources" + + " true" + + " " + + " " + + ""); + + compileModules("project"); + + assertResult("target/classes/file.properties", "value=1\n"); + } + } diff --git a/python/openapi/src/com/jetbrains/python/psi/RootFileReferenceSet.java b/python/openapi/src/com/jetbrains/python/psi/RootFileReferenceSet.java index 8fd92a3b6fd9..a6f1e03b118c 100644 --- a/python/openapi/src/com/jetbrains/python/psi/RootFileReferenceSet.java +++ b/python/openapi/src/com/jetbrains/python/psi/RootFileReferenceSet.java @@ -37,7 +37,7 @@ import java.util.Collection; */ public class RootFileReferenceSet extends FileReferenceSet { public RootFileReferenceSet(String str, - PsiElement element, + @NotNull PsiElement element, int startInElement, PsiReferenceProvider provider, boolean caseSensitive, @@ -46,7 +46,7 @@ public class RootFileReferenceSet extends FileReferenceSet { super(str, element, startInElement, provider, caseSensitive, endingSlashNotAllowed, suitableFileTypes); } - public RootFileReferenceSet(String s, PsiElement element, int offset, PsiReferenceProvider provider, boolean sensitive) { + public RootFileReferenceSet(String s, @NotNull PsiElement element, int offset, PsiReferenceProvider provider, boolean sensitive) { super(s, element, offset, provider, sensitive); } diff --git a/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateFileReferenceSet.java b/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateFileReferenceSet.java index ae67b46c211c..b914ed0c0fe7 100644 --- a/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateFileReferenceSet.java +++ b/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateFileReferenceSet.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -41,11 +41,11 @@ import java.util.List; public class TemplateFileReferenceSet extends FileReferenceSet { private final String mySeparatorString; - public TemplateFileReferenceSet(PsiElement element, @Nullable PsiReferenceProvider provider) { + public TemplateFileReferenceSet(@NotNull PsiElement element, @Nullable PsiReferenceProvider provider) { this(str(element), element, provider); } - public TemplateFileReferenceSet(String text, PsiElement element, + public TemplateFileReferenceSet(String text, @NotNull PsiElement element, @Nullable PsiReferenceProvider provider) { super(text, element, detectShift(element, text), provider, SystemInfo.isFileSystemCaseSensitive); @@ -53,7 +53,7 @@ public class TemplateFileReferenceSet extends FileReferenceSet { reparse(); } - private static String str(PsiElement element) { + private static String str(@NotNull PsiElement element) { return PythonStringUtil.stripQuotesAroundValue(element.getText()); } diff --git a/python/resources/liveTemplates/Python.xml b/python/resources/liveTemplates/Python.xml index a7e57811bb30..96689b16a275 100644 --- a/python/resources/liveTemplates/Python.xml +++ b/python/resources/liveTemplates/Python.xml @@ -205,7 +205,7 @@